python unittest-assertRaises의 반대? 상황에서 예외가 발생하지 않도록 테스트를 작성하고

주어진 상황에서 예외가 발생하지 않도록 테스트를 작성하고 싶습니다.

예외 발생 했는지 테스트하는 것은 간단합니다 …

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

…하지만 어떻게 반대 를 할 수 있습니까 ?

내가 좋아하는이 같은 것 …

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath) 



답변

def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")


답변

안녕-주어진 상황에서 예외가 발생하지 않는지 확인하기 위해 테스트를 작성하고 싶습니다.

이것이 기본 가정입니다. 예외는 발생하지 않습니다.

다른 말을하지 않으면 모든 단일 테스트에서 가정됩니다.

실제로 어떤 주장을 쓰지 않아도됩니다.


답변

그냥 함수를 호출하십시오. 예외가 발생하면 단위 테스트 프레임 워크에서이를 오류로 표시합니다. 다음과 같은 주석을 추가 할 수 있습니다.

sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)


답변

나는 원래 포스터이며 코드에서 먼저 사용하지 않고 DGH의 위 답변을 수락했습니다.

일단 사용한 후에는 실제로 내가해야 할 일을하기 위해 약간의 조정이 필요하다는 것을 깨달았습니다.

다른 사람들의 이익을 위해 여기에 조정을 게시 할 가치가 있다고 생각했습니다.

    try:
        a = Application("abcdef", "")
    except pySourceAidExceptions.PathIsNotAValidOne:
        pass
    except:
        self.assertTrue(False)

여기서 시도한 것은 공백의 두 번째 인수로 Application 객체를 인스턴스화하려고 시도하면 pySourceAidExceptions.PathIsNotAValidOne이 발생하는지 확인하는 것입니다.

위의 코드 (DGH의 답변에 크게 기반)를 사용하면 그렇게 할 것이라고 믿습니다.


답변

모듈 에서 assertNotRaises원래 구현의 약 90 %를 재사용하여 정의 할 수 있습니다 . 이 방법을 사용하면 반전 된 실패 조건을 제외 하고와 동일하게 동작 하는 방법이 생깁니다 .assertRaisesunittestassertNotRaisesassertRaises

TLDR 및 라이브 데모

assertNotRaises메소드 를 추가하는 것은 놀랍게도 쉬운 것으로 판명되었습니다 unittest.TestCase(코드와 마찬가지로이 답변을 작성하는 데 약 4 배가 걸렸습니다). 다음 은 실제 assertNotRaises작동 하는 방법의 데모입니다 . 그냥 같은assertRaises , 당신도에 호출 및 인수를 전달할 수 있습니다 assertNotRaises, 또는 당신은 그것을 사용할 수 있습니다 with문. 라이브 데모에는 assertNotRaises의도 한대로 작동 하는 테스트 사례가 포함되어 있습니다.

세부

assertRaisesin 구현 unittest은 상당히 복잡하지만 약간의 영리한 서브 클래 싱을 사용하면 실패 조건을 무시하고 되돌릴 수 있습니다.

assertRaises기본적으로 unittest.case._AssertRaisesContext클래스 의 인스턴스를 작성 하고 리턴 하는 짧은 메소드입니다 ( unittest.case모듈 의 정의 참조 ). 메소드를 _AssertNotRaisesContext서브 클래 싱 _AssertRaisesContext하고 재정 의하여 고유 한 클래스를 정의 할 수 있습니다 __exit__.

import traceback
from unittest.case import _AssertRaisesContext

class _AssertNotRaisesContext(_AssertRaisesContext):
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is not None:
            self.exception = exc_value.with_traceback(None)

            try:
                exc_name = self.expected.__name__
            except AttributeError:
                exc_name = str(self.expected)

            if self.obj_name:
                self._raiseFailure("{} raised by {}".format(exc_name,
                    self.obj_name))
            else:
                self._raiseFailure("{} raised".format(exc_name))

        else:
            traceback.clear_frames(tb)

        return True

일반적으로 테스트 케이스 클래스를에서 상속하여 테스트 케이스 클래스를 정의합니다 TestCase. 대신 서브 클래스에서 상속하는 경우 MyTestCase:

class MyTestCase(unittest.TestCase):
    def assertNotRaises(self, expected_exception, *args, **kwargs):
        context = _AssertNotRaisesContext(expected_exception, self)
        try:
            return context.handle('assertNotRaises', args, kwargs)
        finally:
            context = None

모든 테스트 사례에 이제 assertNotRaises사용할 수 있는 방법이 있습니다.


답변

def _assertNotRaises(self, exception, obj, attr):
     try:
         result = getattr(obj, attr)
         if hasattr(result, '__call__'):
             result()
     except Exception as e:
         if isinstance(e, exception):
            raise AssertionError('{}.{} raises {}.'.format(obj, attr, exception)) 

매개 변수를 승인해야하는 경우 수정 될 수 있습니다.

처럼 전화

self._assertNotRaises(IndexError, array, 'sort')


답변

unittest다음과 같이 monkey-patch하는 것이 유용하다는 것을 알았습니다 .

def assertMayRaise(self, exception, expr):
  if exception is None:
    try:
      expr()
    except:
      info = sys.exc_info()
      self.fail('%s raised' % repr(info[0]))
  else:
    self.assertRaises(exception, expr)

unittest.TestCase.assertMayRaise = assertMayRaise

이를 통해 예외가 없는지 테스트 할 때 의도가 명확 해집니다.

self.assertMayRaise(None, does_not_raise)

또한 루프에서 테스트를 단순화하여 종종 내가하는 일을 발견합니다.

# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
  self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))