파이썬 모의 클래스 속성
이 문서의 주요 목표는 테스트 및 디버깅 목적으로 Python 단위 테스트 모듈인 unittest
를 사용하여 클래스 특성을 조작하는 방법을 보여 주는 것입니다.
클래스 속성을 조롱하는 이유
버그, 오류 및 코너 케이스에 대해 개발된 코드를 테스트하는 것은 주로 응용 프로그램이 여러 사용자를 대상으로 하는 경우 응용 프로그램을 개발할 때 가장 중요한 측면 중 하나입니다.
내장된 Python 모듈 unittest
를 사용하여 테스트 사례를 수행하여 코드의 무결성을 테스트할 수 있습니다. 엄격한 테스트가 필요한 가장 일반적인 요소 중 하나는 클래스 특성입니다.
클래스 속성은 예기치 않은 동작을 방지하기 위해 무작위 입력을 처리할 수 있습니다.
다음 코드를 고려하십시오.
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
값
이라는 속성과 프로세스
라는 메소드를 포함하는 계산
이라는 클래스를 고려하십시오. 사전은 값
내부에 저장되며 나중에 요구 사항 및 데이터 유형에 따라 처리됩니다.
속성이 거의 모든 유형의 사전을 저장할 수 있고 오류 없이 처리되도록 하려면 속성을 테스트하여 구현에 오류가 없고 수정이 필요하지 않은지 확인해야 합니다.
클래스 속성을 조롱하는 가능한 솔루션
두 가지 방법으로 클래스 속성을 조롱할 수 있습니다. PropertyMock
을 사용하고 PropertyMock
을 사용하지 않습니다. 예제 코드를 사용하여 아래에서 각각 알아봅시다.
PropertyMock
을 사용하여 클래스 속성 모의
속성을 모의하기 위해 주로 속성의 모의 또는 클래스의 설명자로 사용되는 PropertyMock
을 사용할 수 있습니다.
PropertyMock
은 속성을 가져온 후 속성의 반환 값을 변경하기 위해 __get__
및 __set__
메서드를 제공한다는 점은 주목할 가치가 있습니다.
다음 코드를 고려하십시오.
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "value", new_callable=PropertyMock)
def test_method(self, mocked_attrib):
mocked_attrib.return_value = 1
# Will pass without any complaints
self.assertEqual(Calculate().value, 1)
print("First Assertion Passed!")
self.assertEqual(Calculate().value, 22) # Will throw an assertion
print("Second Assertion Passed!")
if __name__ == "__main__":
Test().test_method()
출력:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 24, 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 20, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
솔루션에서 Calculate.value
의 값을 수정하기 위해 test_method
라는 새 메서드가 생성됩니다. 함수는 patch.object
로 데코레이트된다는 점에 유의해야 합니다.
모듈의 patch
데코레이터는 모듈 및 클래스 수준 속성을 패치하는 데 도움이 됩니다. 패치 대상을 좀 더 구체적으로 만들기 위해 patch
대신 patch.object
를 사용하여 메서드를 직접 패치합니다.
데코레이터에서 먼저 Calculate
라는 클래스 이름이 전달되어 패치할 개체가 전달되는 속성 value
의 이름이 있는 Calculate
의 일부임을 나타냅니다.
마지막 매개변수는 PropertyMock
개체로, 다른 숫자를 전달하여 value
속성을 덮어씁니다.
프로그램의 일반적인 흐름은 다음과 같습니다.
test_method
가 호출됩니다.Calculate
클래스의value
가PropertyMock
인스턴스에 할당된new_callable
로 패치됩니다.return_value
속성을 사용하여Calculate.value
를1
로 덮어씁니다.- 첫 번째 어설션이 확인됩니다. 여기서
Calculate.value
는1
과 같아야 합니다. - 두 번째 어설션이 확인됩니다. 여기서
Calculate.value
는22
와 같아야 합니다.
PropertyMock
을 사용하지 않고 클래스 속성 모의
PropertyMock
을 사용하지 않고도 해결할 수 있습니다. 위의 예를 약간만 수정하면 됩니다.
다음 코드를 고려하십시오.
import unittest
from unittest.mock import patch
class Calculate:
value = 22 # Needs to be tested for different data types
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "value", 1)
def test_method(self):
# Will pass without any complaints
self.assertEqual(Calculate().value, 1)
print("First Assertion Passed!")
# Will throw an assertion because "Calculate.value" is now 1
self.assertEqual(Calculate().value, 22)
print("Second Assertion Passed!")
if __name__ == "__main__":
Test().test_method()
출력:
First Assertion Passed!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 23, 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 19, in test_method
self.assertEqual(Calculate().value, 22) # Will throw an assertion because "Calculate.value" is now 1
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 22
PropertyMock
의 인스턴스를 new_callable
에 전달하는 대신 Calculate.value
에 저장하려는 값을 직접 제공할 수 있습니다.
파이썬 모의 클래스 생성자
초기화된 모든 변수가 의도한 대로 작동하고 의도하지 않은 동작을 나타내지 않는지 확인합니다. 코너 케이스를 줄이기 위해 다양한 입력으로 생성자를 테스트하는 것도 필요합니다.
다음 코드를 고려하십시오.
class Calculate:
num = 0
dic = {}
def __init__(self, number, dictt):
self.num = number
self.dic = dictt
def Process(self): # An example method
pass
Calculate
클래스의 생성자를 테스트하고 싶다고 가정해 보겠습니다. 속성이 의도한 대로 작동하도록 하려면 생성자를 패치
하고 가능한 오류를 제거하기 위해 다양한 입력과 함께 전달해야 합니다.
어떻게 할 수 있습니까? 다음 솔루션을 참조하십시오.
patch.object
데코레이터를 사용하여 생성자 패치
patch.object
데코레이터를 사용하여 생성자를 패치할 수 있습니다.
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, PropertyMock
class Calculate:
num = 0
dic = {}
def __init__(self):
self.num = 1
self.dic = {"11", 2}
def Process(self): # An example method
pass
class Test(unittest.TestCase):
@patch.object(Calculate, "__new__")
def test_method(self, mocked_calc):
mocked_instance = MagicMock()
mocked_instance.num = 10
mocked_instance.dic = {"22": 3}
mocked_calc.return_value = mocked_instance
self.assertEqual(Calculate().num, 10)
self.assertEqual(Calculate().dic, {"22": 3})
print("First set of Asserts done!")
self.assertEqual(Calculate().num, 1)
self.assertEqual(Calculate().dic, {"11", 2})
print("Second set of Asserts done!")
if __name__ == "__main__":
Test().test_method()
출력:
The first set of Asserts is done!
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 37, 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 32, in test_method
self.assertEqual(Calculate().num, 1)
File "C:\Program Files\Python310\lib\unittest\case.py", line 845, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Program Files\Python310\lib\unittest\case.py", line 838, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 10 != 1
__init__
을 패치하는 대신 __new__
를 패치했다는 점은 주목할 가치가 있습니다.
클래스의 인스턴스는 __new__
가 실행될 때 생성되지만 __init__
에서는 변수만 초기화되기 때문입니다. 그래서 새로운 모의 인스턴스를 만들어야 하므로 __init__
대신 __new__
를 패치해야 하는 이유는 무엇입니까?
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