파이썬 모의 발생 예외

Salman Mehmood 2023년6월21일
  1. Python에서 단위 테스트 라이브러리 unittest를 사용할 때 예외 발생
  2. 함수에서 수동으로 예외 발생
파이썬 모의 발생 예외

이 문서의 주요 목표는 단위 테스트 라이브러리 unittest를 사용할 때 예외를 throw하는 방법을 보여 주는 것입니다.

Python에서 단위 테스트 라이브러리 unittest를 사용할 때 예외 발생

코드가 안정적이고 프로젝트 범위의 대부분의 사용 사례를 설명하는지 확인하려면 다양한 입력을 제공하여 코드를 테스트해야 합니다. Python에 있는 라이브러리 중 하나인 unittest는 이러한 기능을 제공하여 다른 테스트 사례를 작성하고 수행할 수 있도록 합니다.

테스트하는 동안 입력이 예상치 못한 경우를 처리할 수 있어야 합니다. 주어진 입력에 무엇이 잘못되었는지 자세히 설명하는 예외를 발생시켜 이러한 경우를 처리할 수 있습니다.

다음 코드를 고려하십시오.

class A:
    def __init__(self):
        pass

    def computeData(self):
        return 0


class B:
    def method_to_test():
        obj = A()
        try:
            print(obj.computeData())
        except Exception as e:
            print("Exception at method_to_test: " + str(e))

테스트하려는 함수가 method_to_test라고 가정해 보겠습니다. 다른 클래스 A의 개체를 초기화한 다음 try-except 블록에서 computeData라는 메서드 중 하나를 호출하여 예외를 포착합니다.

특정 예외가 발생했을 때 computeData의 동작을 테스트하려면 unittest 모듈을 사용해야 합니다.

예외를 한 번 발생

위에서 언급한 시나리오를 고려할 때 예외를 throw할 때 더 많은 제어 권한을 갖고 싶은 경우도 있을 수 있습니다.

그러한 요구가 발생할 수 있는 많은 이유가 있을 수 있습니다. 코드 구현에 따라 동작을 더 많이 제어하는 것이 매우 유용할 수 있습니다.

예외 주장

테스트 케이스를 작성하는 동안 예외가 트리거되는지 여부를 확인할 수 있습니다. 이는 예외가 발생했는지 여부를 알고 싶을 때 특히 유용합니다.

이렇게 하면 문제가 발생하는 경우 테스트 사례에서 조기에 발견하는 데 도움이 될 수 있습니다.

모의 타사 모듈

타사 모듈은 구현에 따라 거의 없는 것부터 많은 것까지 다양할 수 있습니다. 타사 모듈이 의도한 대로 작동하는지 테스트하려면 다양한 출력으로 해당 동작을 테스트해야 합니다.

그러한 예 중 하나는 네트워크를 통해 통신하거나 HTTP를 사용하여 데이터를 송수신할 때 널리 사용되는 모듈인 request입니다.

함수에서 수동으로 예외 발생

computeData 함수에서 수동으로 예외를 발생시키려면 side_effect 속성을 사용해야 합니다.

다음 코드를 고려하십시오.

import unittest
from unittest.mock import patch
from unittest.mock import MagicMock


class A:
    def __init__(self) -> None:
        pass

    def computeData(self):
        return 0


class B:
    def method_to_test():
        obj = A()
        try:
            print(obj.computeData())
        except Exception as e:
            print("Exception at method_to_test: " + str(e))


class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
    def test_method(self):
        B.method_to_test()


if __name__ == "__main__":
    Test().test_method()

출력:

Exception at method_to_test: Test

솔루션에서 method_to_test 메서드를 테스트하기 위해 새 메서드 test_method가 생성됩니다. 함수가 patch.object로 장식되어 있다는 점도 중요합니다.

patch 데코레이터는 모듈 및 클래스 수준 특성을 패치하기 위해 모듈에 제공됩니다. 무엇을 패치할지 구체적으로 설명하기 위해 patch 대신 patch.object를 사용하여 메서드를 직접 패치합니다.

데코레이터에서 클래스 이름 A가 전달됩니다. 이는 패치할 개체가 멤버 이름 computeData가 전달되는 A의 일부임을 나타냅니다.

마지막 매개변수는 Mock의 하위 클래스인 MagicMock 개체입니다. 여기에서 side_effect 속성을 사용하여 필요한 동작을 computeData에 연결합니다. 이 속성은 우리의 경우 예외를 발생시킵니다.

프로그램의 일반적인 흐름은 다음과 같습니다.

  1. test_method가 호출됩니다.
  2. A 클래스의 computeData가 패치됩니다. side_effect가 할당되는데, 우리의 경우에는 예외입니다.
  3. 클래스 Bmethod_to_test가 호출됩니다.
  4. obj에 저장된 인스턴스와 함께 A 클래스의 생성자가 호출됩니다.
  5. computeData가 호출됩니다. 패치 중인 메서드로 인해 예외가 throw됩니다.

한 번 예외 발생

예외를 한 번만 발생시키거나 함수의 동작을 더 많이 제어하기 위해 side_effect는 단순한 함수 호출 이상을 지원합니다.

현재 side_effect는 다음을 지원합니다.

  1. 반복 가능
  2. 호출 가능
  3. 예외(인스턴스 또는 클래스)

Iterable을 사용하면 예외를 한 번만 발생시키는 메서드를 만들 수 있습니다.

class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=[1, Exception("Test"), 3]))
    def test_method(self):
        B.method_to_test()


if __name__ == "__main__":
    testClass = Test()
    testClass.test_method()
    testClass.test_method()
    testClass.test_method()

출력:

1
Exception at method_to_test: Test
3

전달된 Iterable로 인해 선택한 요소가 패치되고 예외를 발생시킬 시기를 제어할 수 있습니다. 따라서 함수가 두 번째로 호출될 때만 예외가 발생하고 다른 경우에는 13이 반환됩니다.

예외 주장

특정 예외가 발생했는지 여부를 확인하려면 원하는 예외가 발생하지 않은 경우 unittest.TestCase.assertRaises를 사용할 수 있습니다.

class Test(unittest.TestCase):
    @patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
    def test_method(self):
        self.assertRaises(KeyError, A.computeData)


if __name__ == "__main__":
    Test().test_method()

출력:

Exception at method_to_test: Test
Traceback (most recent call last):
  File "d:\Python Articles\a.py", line 28, in <module>
    Test().test_method()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
  File "d:\Python Articles\a.py", line 25, in test_method
    self.assertRaises(KeyError, A.computeData)
  File "C:\Program Files\Python310\lib\unittest\case.py", line 738, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Program Files\Python310\lib\unittest\case.py", line 201, in handle
    callable_obj(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
    return self._mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
    raise effect
  File "d:\Python Articles\a.py", line 17, in method_to_test
Exception: Test

두 개의 매개변수로 assertRaises를 호출하여 test_method 메서드를 약간 변경하여 위의 결과를 얻었습니다.

두 매개변수는 다음과 같이 정의할 수 있습니다.

  1. KeyError - 트리거하려는 모든 예외가 여기에 전달됩니다.
  2. A.computeData - 예외를 throw해야 하는 메서드입니다.

모의 타사 모듈

이전과 같이 post를 조롱하려면 patch 데코레이터를 사용해야 합니다. 간단한 예는 다음과 같습니다.

class Test(unittest.TestCase):
    @patch(
        "requests.post", MagicMock(side_effect=requests.exceptions.ConnectionError())
    )
    def test_method(self):
        requests.post()

출력:

Traceback (most recent call last):
  File "d:\Python Articles\a.py", line 33, in <module>
    Test().test_method()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
  File "d:\Python Articles\a.py", line 30, in test_method
    requests.post()
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
    return self._mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
  File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
    raise effect
requests.exceptions.ConnectionError

제3자 모듈의 기능은 패치 데코레이터를 사용하여 패치할 수 있으므로 제품/프로그램의 안정성 수준에 대한 더 나은 디버깅 및 제어가 가능합니다.

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

관련 문장 - Python Exception

관련 문장 - Python Unit Test