programing

Python unittest - assertRaises의 반대?

sourcejob 2022. 11. 5. 17:31
반응형

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)

코멘트에서 설명을 추가하기 위해 편집:

  • 유닛 테스트에는 합격, 불합격, 오류의 3가지 결과가 있습니다.(실제로 XPass/XFail/Skip을 세면 더 많은 결과가 나옵니다...)
  • 테스트 중인 특정 예외가 느려지지 않고 느려지는 경우 이론적으로는 실패입니다.그러나 위의 코드는 이론적으로 "잘못된" 오류입니다.
  • 실제로 에러가 발생하면 테스트 실행자가 스택트레이스를 출력할 수 있기 때문에 장애 디버깅에 도움이 될 수 있습니다.Fail(실패)의 경우 스택 트레이스는 표시되지 않을 수 있습니다.
  • 실제로 불합격의 경우 테스트를 "예상 불합격"으로 표시할 수 있습니다.에러에서는, 테스트를 「건너뛰기」라고 마크 할 수 있습니다만, 그렇게 할 수 없습니다.
  • 실제적인 문제로서 테스트 사례를 에러로 보고하려면 추가 코드가 필요합니다.
  • "오류"와 "실패"의 차이는 프로세스에 따라 달라집니다.우리 팀이 유닛 테스트를 사용하는 방법으로는 모두 합격해야 합니다.(모든 유닛 테스트를 실행하는 연속 통합 기계와 함께 민첩한 프로그래밍).우리 팀에게 실제로 중요한 것은 "모든 유닛 테스트에 합격했는가?"(즉, "젠킨스는 녹색인가?")입니다.따라서 우리 팀에서는 "실패"와 "오류" 사이에 실질적인 차이는 없습니다.
  • 위의 이점(코드 감소, 스택트레이스 표시)과 Fail/Error가 팀에 의해 동일하게 취급되기 때문에 이 방법을 사용합니다.
  • 특히 프로세스가 "실패"와 "오류"를 다르게 처리하거나 테스트를 "예상 실패"로 표시하려는 경우 유닛 테스트를 다른 방식으로 사용하는 경우 요구 사항이 달라질 수 있습니다.
  • 이 테스트에서 오류를 보고하려면 DGH의 답변을 사용하십시오.

저는 오리지널 포스터이며, DGH의 답변을 코드에 처음 사용하지 않고 받아들였습니다.

사용해보니 실제로 필요한 것을 하기 위해서는 약간의 조정이 필요하다는 것을 깨달았습니다(DGH에게 공평하게 말하자면, 「또는 비슷한 것」이라고 말했습니다).

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

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

여기서 하려고 했던 것은 어플리케이션오브젝트를 두 번째 인수로 인스턴스화하려고 하면 pySourceAid가예외입니다.패스 없음AValidOne이 발생합니다.

저는 (DGH의 답변에 따라) 위의 코드를 사용하는 것이 좋다고 생각합니다.

정의할 수 있습니다.assertNotRaises원래 구현의 약 90%를 재사용함으로써assertRaises에서unittest모듈.이 접근방식에서는 최종적으로는assertNotRaises역고장 조건을 제외하고 동일하게 동작하는 방법assertRaises.

TLDR 및 라이브 데모

그 결과, 이 데이터를 추가하는 것은 놀라울 정도로 쉬운 것으로 밝혀졌습니다.assertNotRaises하는 방법unittest.TestCase(이 답을 쓰는 데 코드보다 약 4배 더 오래 걸렸습니다.)다음은 작동 중인 방법을 보여 주는 라이브 데모입니다.마찬가지로 콜 가능 및 arg를 에 전달할 수 있습니다.assertNotRaises또는 에서 사용할 수 있습니다.with진술.라이브 데모에는 다음과 같은 것을 증명하는 테스트 케이스가 포함되어 있습니다.assertNotRaises정상적으로 동작합니다.

세부 사항

의 실장assertRaisesunittest상당히 복잡하지만 약간의 교묘한 하위 분류를 통해 장애 상태를 덮어쓰거나 되돌릴 수 있습니다.

assertRaises간단한 방법으로 기본적으로 의 인스턴스를 만듭니다.unittest.case._AssertRaisesContextclass 및 return 명령어(의 정의를 참조해 주세요.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 method available to them.

I've found it useful to monkey-patch unittest as follows:

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

This clarifies intent when testing for the absence of an exception:

self.assertMayRaise(None, does_not_raise)

This also simplifies testing in a loop, which I often find myself doing:

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

could be modified if you need to accept parameters.

call like

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

If you pass an Exception class to assertRaises(), a context manager is provided. This can improve the readability of your tests:

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

This allows you to test error cases in your code.

In this case, you are testing the PathIsNotAValidOne is raised when you pass invalid parameters to the Application constructor.

you can try like that. try: self.assertRaises(None,function,arg1, arg2) except: pass if you don't put code inside try block it will through exception' AssertionError: None not raised " and test case will be failed. Test case will be pass if put inside try block which is expected behaviour.

One straight forward way to ensure the object is initialized without any error is to test the object's type instance.

Here is an example :

p = SomeClass(param1=_param1_value)
self.assertTrue(isinstance(p, SomeClass))

ReferenceURL : https://stackoverflow.com/questions/4319825/python-unittest-opposite-of-assertraises

반응형