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


답변:


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.exceptionstr, 그렇지 않으면 당신이를 얻을 수 있습니다,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있습니다. 경우 afunctionself.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.