파이썬 함수가 예외를 던지는 지 어떻게 테스트합니까?


답변:


679

다음 과 같이 unittest 모듈에서 TestCase.assertRaises(또는 TestCase.failUnlessRaises)를 사용하십시오 .

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

9
이것의 반대를 할 수있는 방법이 있습니까? 함수가 예외를 던질 때만 실패하는 것처럼?
— BUInvent

67
인수를 전달 myfunc하려면 assertRaises 호출에 인수로 인수를 추가해야합니다. Daryl Spitzer의 답변을 참조하십시오.
— cbron

1
여러 예외 유형을 허용하는 방법이 있습니까?
— Dinesh

1
Python의 내장 예외를 사용하여 어설 션을 빠르게 테스트 할 수 있습니다. 위의 @Moe의 대답을 사용하십시오 (예 :) self.assertRaises(TypeError, mymod.myfunc). 당신은 여기에 내장 예외의 전체 목록을 찾을 수 있습니다 docs.python.org/3/library/exceptions.html#bltin-exceptions을
— 레이몬드 Wachaga에게

3
클래스 생성자를 테스트 할 self.assertRaises(SomeCoolException, Constructor, arg1)
— 때도 마찬가지입니다

476

Python 2.7부터 컨텍스트 관리자를 사용하여 실제로 발생한 Exception 객체를 파악할 수 있습니다.

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


에서 파이썬 3.5 , 당신은 포장이 context.exception에 str, 그렇지 않으면 당신이를 얻을 수 있습니다,TypeError

self.assertTrue('This is broken' in str(context.exception))

6
Python 2.7.10을 사용하고 있는데 위의 기능이 작동하지 않습니다. context.exception메시지를 제공하지 않습니다. 유형입니다.
— LateCoder

6
또한 Python 2.7 (적어도 내 2.7.6에서는)에서을 import unittest2사용해야합니다 str(). 즉 self.assertTrue('This is broken' in str(context.exception)).
— Sohail Si

4
두 가지 : 1. assertTrue 대신 assertIn을 사용할 수 있습니다. 예 : self.assertIn ( 'This is broken', context.exception) 2. 2.7.10을 사용하는 경우 context.exception은 문자 배열 인 것으로 보입니다. str을 사용하면 작동하지 않습니다. 나는 이것을 끝내었다 : ''.join (context.exception) 그래서, 함께 정리하십시오 : self.assertIn ( 'This is broken', ''.join (context.exception))
— blockcipher

1
메소드가 예외 추적으로 테스트 콘솔을 닫는 것이 정상입니까? 그런 일이 발생하지 않도록하려면 어떻게합니까?
— MadPhysicist

1
나중에 예외의 str으로 메시지를 얻는 다른 방법을 찾았습니다 .err = context.exception.message입니다. 그런 다음 self.assertEqual (err, 'This is broken')을 사용하여 테스트를 수행 할 수도 있습니다.
— zhihong

326

이전 답변의 코드는 다음과 같이 단순화 할 수 있습니다.

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

그리고 함수가 인수를받는다면, 다음과 같이 assertRaises에 전달하십시오 :

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

17
두 번째는 인수가 전달 될 때 무엇을해야하는지에 대해 아는 것이 정말 도움이되었습니다.
— Sabyasachi

을 사용하고 2.7.15있습니다. 경우 afunction에 self.assertRaises(ExpectedException, afunction, arg1, arg2)클래스 초기화, 당신은 통과해야 self첫 번째 인수의 예를 들면,로 self.assertRaises(ExpectedException, Class, self, arg1, arg2)
— 호치민 트란

128

파이썬 함수가 예외를 던지는 지 어떻게 테스트합니까?

함수가 예상 예외를 throw하지 않는 경우에만 실패하는 테스트를 작성하는 방법은 무엇입니까?

짧은 답변:

이 self.assertRaises메소드를 컨텍스트 관리자로 사용하십시오 .

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

데모

모범 사례 접근 방식은 Python 셸에서 쉽게 보여줄 수 있습니다.

unittest라이브러리

Python 2.7 또는 3에서 :

import unittest

Python 2.6에서는 unittest2unittest 라는 2.7 라이브러리 의 백 포트를 설치하고 다음 과 같이 별명을 지정할 수 있습니다 .unittest

import unittest2 as unittest

테스트 예

이제 다음과 같은 Python 유형 안전성 테스트를 Python 셸에 붙여 넣습니다.

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

하나를 테스트 assertRaises 컨텍스트 관리자로 하여 기록되는 동안 오류가 올바르게 포착 및 정리되도록합니다.

컨텍스트 관리자 없이도 작성할 수 있습니다 ( 테스트 2 참조). 첫 번째 인수는 발생할 것으로 예상되는 오류 유형, 두 번째 인수, 테스트중인 함수 및 나머지 args 및 키워드 args가 해당 함수에 전달됩니다.

컨텍스트 관리자를 사용하는 것이 훨씬 간단하고 읽기 쉽고 유지 관리가 가능하다고 생각합니다.

테스트 실행

테스트를 실행하려면

unittest.main(exit=False)

Python 2.6에서는 다음이 필요할 것입니다 .

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

그리고 터미널은 다음을 출력해야합니다 :

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

그리고 우리는 예상대로 1a에 '1'결과 를 추가하려고 시도 합니다 TypeError.


더 자세한 출력을 보려면 다음을 시도하십시오.

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

56

코드는 다음 패턴을 따라야합니다 (이것은 단위 테스트 모듈 스타일 테스트입니다).

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

Python <2.7에서이 구문은 예상되는 예외에서 특정 값을 확인하는 데 유용합니다. unittest 함수 assertRaises는 예외가 발생했는지 확인합니다.


3
self.fail 메소드는 단 하나의 인수를 취합니다
— mdob

3
함수가 예외를 던지면 테스트하기에는 지나치게 복잡해 보입니다. 해당 예외 이외의 예외는 테스트에 오류를 발생시키고 예외를 throw하지 않으면 테스트에 실패하므로 유일한 차이점은 다른 예외를 받으면 assertRaisesFAIL 대신 ERROR를 얻는 것입니다.
— unflores

23

에서 : http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

먼저, dum_function.py 파일에 해당하는 (여전히 d : p) 함수가 있습니다 :

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

수행 할 테스트는 다음과 같습니다 (이 테스트 만 삽입 됨).

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

   if __name__ == "__main__":
       unittest.main()

이제 기능을 테스트 할 준비가되었습니다! 테스트를 실행하려고 할 때 다음과 같이됩니다.

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

TypeError가 actullay 발생하고 테스트 실패를 생성합니다. 문제는 이것이 바로 우리가 원하는 행동이라는 것입니다.

이 오류를 피하려면 테스트 호출에서 lambda를 사용하여 함수를 실행하십시오.

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

최종 출력 :

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

완벽 해!

... 나도 완벽합니다!

Mr. Julien Lengrand-Lambert 씨 감사합니다


이 테스트 어설 션은 실제로 오 탐지를 반환합니다 . 'assertRaises'내부의 람다 는 테스트 된 함수가 아닌 형식 오류를 발생시키는 단위이기 때문에 발생 합니다.


10
참고로, 람다가 필요하지 않습니다. 라인 self.assertRaises(TypeError, df.square_value(self.false_int))은 메소드를 호출하고 결과를 리턴합니다. 당신이 원하는 것은 메소드와 인수를 전달하고 unittest가 그것을 호출하게하는 것입니다 :self.assertRaises(TypeError, df.square_value, self.false_int)
— Roman Kutlak

고마워 완벽하게 작동
— Chandan Kumar

14

contextmanager예외가 발생했는지 확인 하기 위해 직접 빌드 할 수 있습니다 .

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

그런 다음 다음 raises과 같이 사용할 수 있습니다 .

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

을 사용하는 경우 pytest이미 구현되어 있습니다. 넌 할 수있어pytest.raises(Exception) :

예:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

그리고 결과 :

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

1
unittest모듈이 필요없는 답변을 게시 해 주셔서 감사 합니다!
— Sherwood Callaway

10

나는 함수를 문서화하고 동시에 테스트한다는 사실을 좋아하기 때문에 거의 모든 곳에서 doctest [1]를 사용 한다.

이 코드를 살펴보십시오.

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

이 예제를 모듈에 넣고 명령 줄에서 실행하면 두 테스트 사례가 모두 평가되고 확인됩니다.

[1] 파이썬 문서 : 23.2 doctest-대화식 파이썬 예제 테스트


4
나는 doctest를 좋아하지만 unittest를 대체하기보다는 보충제를 찾으십시오.
— TimothyAWiseman

2
자동 리팩토링으로 doctest가 잘 재생되지 않습니까? 나는 파이썬을 위해 디자인 된 리팩토링 도구를 가정 한다 문서화 문자열을 인식. 누구나 자신의 경험에서 의견을 말할 수 있습니까?
— kdbanman 2016 년

6

방금 Mock 라이브러리 가 assertRaisesWithMessage () 메소드 (unittest.TestCase 서브 클래스에 있음)를 제공 한다는 것을 발견했습니다.이 메소드는 예상 예외가 발생했는지뿐만 아니라 예상 메시지가 발생했는지 확인합니다.

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

불행히도, 그것은 더 이상 그것을 제공하지 않습니다. 그러나 위의 @Art ( stackoverflow.com/a/3166985/1504046 )의 대답은 같은 결과를 낳습니다
— Rmatt

6

여기에 많은 답변이 있습니다. 이 코드는 예외를 생성하는 방법, 메소드에서 해당 예외를 사용하는 방법 및 마지막으로 단위 테스트에서 올바른 예외가 발생하는지 확인하는 방법을 보여줍니다.

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


if __name__ == '__main__':
    unittest.main()

3

unittest 모듈에서 assertRaises를 사용할 수 있습니다

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

-5

모든 대답이 완벽하게 훌륭하지만 단위 테스트 프레임 워크에 의존하지 않고 테스트 클래스를 작성하지 않고도 함수에서 예외가 발생했는지 테스트하는 방법을 찾고있었습니다.

나는 다음을 작성했다.

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

그리고 그것은 올바른 줄에 실패합니다 :

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.