python unittest-assertRaises의 반대?


374

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

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

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

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

내가 좋아하는이 같은 것 ...

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

6
항상 테스트에서 작동해야하는 모든 것을 간단하게 수행 할 수 있습니다. 오류가 발생하면 오류가 아닌 오류로 간주됩니다. 물론 그것은 정의 된 유형의 오류가 아니라 오류를 발생시키지 않는다고 가정합니다. 그 외에는 직접 작성해야 할 것 같습니다.
토마스 K

1
가능한 파이썬
brandizzi

실제로 ~ 30-ish 코드 줄에서 assertNotRaises코드 / 동작의 90 %를 공유 하는 메소드를 구현할 수 있습니다 assertRaises. 자세한 내용은 아래 답변을 참조 하십시오.
tel

hypothesis원본이 예외를 일으키는 경우를 무시하면서 두 종류의 함수를 비교 하여 모든 종류의 입력에 대해 동일한 출력을 생성 할 수 있도록하기 위해 이것을 원합니다 . assume(func(a))출력이 모호한 진리 값을 가진 배열이 될 수 있기 때문에 작동하지 않습니다. 그래서 함수를 호출하고 True실패하지 않으면 얻을 수 있습니다. assume(func(a) is not None)내가 추측하는 작품
endolith

답변:


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

32
@hiwaylon-아니요, 사실 올바른 해결책입니다. user9876이 제안한 솔루션은 개념적으로 결함이 있습니다. say의 비 제기를 테스트 ValueError하지만 ValueError대신 제기되면 테스트는 오류가 아닌 실패 조건으로 종료해야합니다. 반면에 동일한 코드를 실행하는 경우 KeyError오류가 아닌 오류가 발생합니다. 파이썬에서는 다른 언어와 달리 제어 흐름에 예외가 일상적으로 사용되므로 except <ExceptionName>구문이 실제로 있는 이유 입니다. 이와 관련하여 user9876의 솔루션은 단순히 잘못되었습니다.
mac

@mac-이것도 올바른 해결책입니까? stackoverflow.com/a/4711722/6648326
MasterJoe2

이것은 테스트에 대해 <100 % 적용 범위 (제외되지 않을 것임)를 표시하는 불행한 효과가 있습니다.
Shay

3
@Shay, IMO 당신은 항상 커버리지 보고서에서 테스트 파일 자체 (그들은 거의 항상 정의에 의해 100 % 실행, 당신은 인위적으로 보고서를 올리는 것)을 제외해야
기존 BBQ 소스

@ original-bbq-sauce는 의도하지 않은 건너 뛴 테스트를 열지 않습니다. 예를 들어 테스트 이름의 오타 (ttst_function), pycharm의 잘못된 실행 구성 등?
Shay

67

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

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

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

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


7
@IndradhanushGupta 잘 받아 들여지는 대답은이 것보다 시험을 더 비석으로 만듭니다. 암시적인 것보다 명시적인 것이 좋습니다.
0xc0de

17
user9876의 대답이 틀린 것과 같은 이유이지만 다른 주석 자는이 답변이 왜 틀린지 지적하지 않았습니다. 실패와 오류는 테스트 코드에서 다릅니다. 함수가있는 경우 하였다 없는 주장 않는 검사 도중 예외를 던져, 테스트 프레임 워크는로 그 치료 것 오류가 오히려하지 주장 실패보다.
coredumperror

@CoreDumpError 실패와 오류의 차이점을 이해하지만 시도 / 예외 구성으로 모든 테스트 를 포괄하도록 강제하지 않습니까? 또는 특정 조건에서 예외를 명시 적으로 발생시키는 테스트 (기본적으로 예외가 예상 됨 )에 대해서만 수행하는 것이 좋습니다 .
federicojasson

4
@federicojasson 두 번째 문장에서 자신의 질문에 아주 잘 대답했습니다. 테스트에서 오류와 실패는 각각 "예기치 않은 충돌"과 "의도하지 않은 동작"으로 간결하게 설명 할 수 있습니다. 함수가 충돌 할 때 테스트에 오류가 표시되기를 원하지만 다른 입력이 주어지면 특정 입력에 대해 예외가 발생한다는 것을 알고있는 예외는 아닙니다.
coredumperror 2018 년

52

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

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

35
실패와 오류는 개념적으로 다릅니다. 또한, 파이썬에서는 예외적으로 제어 흐름에 예외가 사용되기 때문에 논리 나 코드를 깨뜨린 경우 한 눈에 이해하기가 매우 어렵습니다 (= 테스트 코드를 탐색하지 않음).
mac

1
시험에 합격했거나 통과하지 못했습니다. 통과하지 못하면 해결해야합니다. "실패"또는 "오류"로보고되는지 여부는 대부분 관련이 없습니다. 한 가지 차이점이 있습니다. 내 대답에는 스택 추적이 표시되어 PathIsNotAValidOne이 발생한 위치를 확인할 수 있습니다. 허용 된 답변을 사용하면 해당 정보가 없으므로 디버깅이 어려워집니다. (Py2라고 가정하면 Py3이 더 나은지 확실하지 않습니다).
user9876

19
@ user9876-아뇨. 테스트 종료 조건은 3 (통과 / 통과 / 오류)이며 잘못 판단한 것처럼 2가 아닙니다. 오류와 실패의 차이는 상당하며 오류를 동일하게 처리하는 것은 프로그래밍이 좋지 않은 것입니다. 당신이 나를 믿지 않는다면, 테스트 러너가 작동하는 방식과 실패와 오류로 인해 어떤 의사 결정 트리가 구현되는지 살펴보십시오. 파이썬의 좋은 출발점 은 pytest xfail데코레이터 입니다.
mac

4
단위 테스트 사용 방법에 달려 있다고 생각합니다. 우리 팀이 단위 테스트를 사용하는 방식은 모두 통과해야합니다. (모든 단위 테스트를 실행하는 지속적인 통합 시스템을 갖춘 민첩한 프로그래밍). 테스트 사례가 "통과", "실패"또는 "오류"를보고 할 수 있음을 알고 있습니다. 그러나 높은 수준에서 우리 팀에게 실제로 중요한 것은 "모든 단위 테스트를 통과합니까?" (즉 "Jenkins는 녹색입니까?"). 따라서 우리 팀에게는 "실패"와 "오류"사이에 실질적인 차이가 없습니다. 단위 테스트를 다른 방식으로 사용하는 경우 요구 사항이 다를 수 있습니다.
user9876

1
@ user9876 '실패'와 '오류'의 차이는 "내 주장에 실패했습니다"와 "내 테스트가 주장에 도달하지도 않음"의 차이입니다. 그것은 테스트를 고치는 동안 유용한 차이점이지만, 당신이 말하는 것처럼 모든 사람에게 해당되는 것은 아닙니다.
CS

14

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

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

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

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

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

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


2
질문을 명확하게하고 답변하지 않기 때문에 편집해야합니다 (답변은 아님). 아래 답변을 참조하십시오.
hiwaylon

13
원래 문제와는 정반대 인 것 같습니다. self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath)이 경우 작업을 수행해야합니다.
Antony Hatchkins

8

모듈 에서 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사용할 수 있는 방법이 있습니다.


traceback당신의 else진술 에서 당신 은 어디 에서 오는가?
NOhs

1
@NOhs 누락되었습니다 import. 수정 됨
Tel

2
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')

1

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))

원숭이 패치 란?
ScottMcC

1
en.wikipedia.org/wiki/Monkey_patch를 참조하십시오 . 추가 한 다음 assertMayRaiseunittest.TestSuite당신 만의 그것의 일부 척 수있는 unittest라이브러리를.
AndyJost

0

에 예외 클래스를 전달하면 assertRaises()컨텍스트 관리자가 제공됩니다. 이렇게하면 테스트의 가독성이 향상 될 수 있습니다.

# raise exception if Application created with bad data
with self.assertRaises(pySourceAidExceptions.PathIsNotAValidOne):
    application = Application("abcdef", "")

이를 통해 코드에서 오류 사례를 테스트 할 수 있습니다.

이 경우 PathIsNotAValidOne응용 프로그램 생성자에 잘못된 매개 변수를 전달할 때 발생 하는 테스트 입니다.


1
아니요, 컨텍스트 관리자 블록 내에서 예외 발생 하지 않는 경우에만 실패합니다 . 'self.assertRaises (TypeError) : raise TypeError'를 사용하여 쉽게 테스트 할 수 있습니다.
Matthew Trevor

@MatthewTrevor 좋은 전화. 내가 기억하는 것처럼 테스트 코드가 올바르게 실행되지 않고 (즉, 제기되지 않음) 테스트 오류 사례를 제안하고있었습니다. 그에 따라 답변을 편집했습니다. 잘만되면 나는 빨강에서 나가기 위해 +1을 벌 수있다. :)
hiwaylon

참고 : 이것은 파이썬 2.7 이상입니다 : docs.python.org/2/library/…
qneill

0

당신은 그렇게 시도 할 수 있습니다. try : self.assertRaises (None, function, arg1, arg2) 예외 : try 블록 안에 코드를 넣지 않으면 'AssertionError : None not raised'예외를 통해 테스트 케이스가 실패합니다. 시도 블록 안에 넣으면 예상되는 동작입니다.


0

객체가 오류없이 초기화되도록하는 한 가지 직접적인 방법은 객체의 유형 인스턴스를 테스트하는 것입니다.

예를 들면 다음과 같습니다.

p = SomeClass(param1=_param1_value)
self.assertTrue(isinstance(p, SomeClass))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.