pytest-mock의 사용법
1. 기본 사용 방법:
1.1 설치 방법
- 설치 : pip install pytest-mock
1.2 간단한 코드 사용법
- mock으로 사용할 target 함수 정의
tests/file.py
def target_mock():
return "mock : True"
- mock을 사용할 코드 정의
# tests/test_mock.py
from tests import file
def test_mock(mocker):
mocker.patch("tests.file.target_mock", return_value="mock : False")
return = file.target_mock()
original = "mock : False"
assert actual == expected
- 테스트의 결과는 성공으로 나온다 mocker.patch에서 return 값을 변경했기 때문이다.
2 mocker.patch와 mocker.patch.object에 대해
- pytest에서 'mocker'는 개체나 함수를 테스트 double로 대체하는 강력하고 유연한 방법을 제공하는 내장 고정물입니다. 개체 및 함수를 패치하기 위해 mocker 고정 장치에서 제공하는 일반적으로 사용되는 두 가지 방법은 mocker.patch() 및 mocker.patch.object()입니다.
2.1 mocker.patch()
- mocker.patch() 메서드를 사용하면 가져오기 경로를 문자열로 지정하여 객체나 함수를 패치할 수 있습니다. 이는 외부 모듈에 정의된 개체 또는 함수를 교체하려는 경우에 유용할 수 있습니다.
예를 들면 다음과 같습니다.
from my_module import my_function
def test_my_function(mocker):
mock_function = mocker.Mock(return_value=42)
mocker.patch('my_module.my_function', mock_function)
result = my_function()
assert result == 42
mock_function.assert_called_once()
- 이 예제에서는 my_module 모듈에서 my_function을 가져오고 mocker.Mock()을 사용하여 모의 함수를 만듭니다. 그런 다음 mocker.patch()를 사용하여 my_function을 모의 함수로 대체합니다. 마지막으로 my_function()을 호출하고 결과가 모의 함수가 반환한 값인 42임을 확인합니다.
2.2 mocker.patch.object()
- mocker.patch.object() 메서드를 사용하면 객체의 속성을 테스트 더블로 패치할 수 있습니다. 이는 개체의 메서드를 대체하려는 경우에 유용할 수 있습니다.
예를 들면 다음과 같습니다.
class MyClass:
def my_method(self):
return 42
def test_my_method(mocker):
my_object = MyClass()
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(my_object, 'my_method', mock_method)
result = my_object.my_method()
assert result == 24
mock_method.assert_called_once()
- 이 예제에서는 42를 반환하는 my_method() 메서드로 MyClass 클래스를 정의합니다. 그런 다음 mocker.Mock()을 사용하여 MyClass 인스턴스와 모의 메서드를 만듭니다. mocker.patch.object()를 사용하여 my_object의 my_method() 메서드를 모의 메서드로 대체합니다. 마지막으로 my_object.my_method()를 호출하고 결과가 모의 메서드가 반환한 값인 24임을 확인합니다.
3 mocker.patch()의 5개 샘플 코드
3.1 HTTP 요청을 만드는 기능 테스트HTTP 요청을 만드는 기능 테스트
import requests
def my_function():
response = requests.get('http://example.com')
return response.status_code
def test_my_function(mocker):
mock_response = mocker.Mock()
mock_response.status_code = 200
mocker.patch('requests.get', return_value=mock_response)
result = my_function()
assert result == 200
- 이 시나리오에서는 requests 라이브러리를 사용하여 HTTP 요청을 만드는 my_function() 함수를 테스트하고 있습니다.
- mocker.patch()를 사용하여 requests.get() 함수를 mock_response 객체를 반환하는 모의 함수로 대체합니다.
- 그런 다음 my_function()이 예상 상태 코드를 반환하는 것을 검사합니다.
3.2 데이터베이스 연결을 사용하는 기능 테스트
import psycopg2
def my_function():
conn = psycopg2.connect(dbname='mydatabase')
cursor = conn.cursor()
cursor.execute('SELECT COUNT(*) FROM mytable')
count = cursor.fetchone()[0]
conn.close()
return count
def test_my_function(mocker):
mock_cursor = mocker.Mock()
mock_cursor.fetchone.return_value = (42,)
mock_conn = mocker.Mock()
mock_conn.cursor.return_value = mock_cursor
mocker.patch('psycopg2.connect', return_value=mock_conn)
result = my_function()
assert result == 42
- 이 시나리오에서는 psycopg2 라이브러리를 사용하여 PostgreSQL 데이터베이스에 연결하는 my_function() 함수를 테스트하고 있습니다.
- 우리는 mocker.patch()를 사용하여 psycopg2.connect() 함수를 mock_conn 객체를 반환하는 모의 함수로 대체합니다.
- 그런 다음 모의 커서를 만들고 이를 사용하여 데이터베이스 쿼리를 모의 처리합니다.
- 마지막으로 my_function()이 예상 개수를 반환하는 것을 검사합니다.
3.3 타사 라이브러리를 사용하는 기능 테스트
from my_module import some_function
def my_function():
value = some_function()
return value * 2
def test_my_function(mocker):
mock_some_function = mocker.Mock(return_value=21)
mocker.patch('my_module.some_function', mock_some_function)
result = my_function()
assert result == 42
- 이 시나리오에서는 my_module에서 가져온 타사 라이브러리 함수 some_function()을 사용하는 my_function() 함수를 테스트하고 있습니다.
- 우리는 mocker.patch()를 사용하여 my_module.some_function()을 21을 반환하는 모의 함수로 대체합니다.
- 그런 다음 my_function()이 예상 값을 반환하는 것을 검사합니다.
3.4 복잡한 객체를 사용하는 함수 테스트
class MyClass:
def my_method(self):
return 42
def my_function():
my_object = MyClass()
return my_object.my_method()
def test_my_function(mocker):
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(MyClass, 'my_method', mock_method)
result = my_function()
assert result == 24
- 이 시나리오에서는 MyClass 인스턴스를 사용하고 my_method() 메서드를 호출하는 my_function() 함수를 테스트하고 있습니다.
- mocker.patch.object()를 사용하여 MyClass의 my_method() 메서드를 24를 반환하는 모의 메서드로 바꿉니다.
- 그런 다음 my_function()이 예상 값을 반환하는 것을 검사합니다.
3.5 임의의 값을 생성하는 함수 테스트
import random
def my_function():
value = random.randint(1, 10)
return value * 2
def test_my_function(mocker):
mocker.patch('random.randint', return_value=5)
result = my_function()
assert result == 10
- 이 시나리오에서는 random 모듈을 사용하여 임의의 값을 생성하는 my_function() 함수를 테스트하고 있습니다.
- 우리는 mocker.patch()를 사용하여 random.randint() 함수를 항상 5를 반환하는 모의 함수로 대체합니다.
- 그런 다음 my_function()이 예상 값을 반환하는 것을 검사합니다.
4 mocker.object()의 5개 샘플 코드
4.1 객체의 메서드 테스트
class MyClass:
def my_method(self):
return 42
def test_my_method(mocker):
my_object = MyClass()
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(my_object, 'my_method', mock_method)
result = my_object.my_method()
assert result == 24
- 이 시나리오에서는 MyClass 인스턴스의 my_method() 메서드를 테스트하고 있습니다.
- 우리는 mocker.patch.object()를 사용하여 my_object의 my_method() 메서드를 24를 반환하는 모의 메서드로 바꿉니다. - 그런 다음 my_object.my_method()가 예상 값을 반환하는 것을 검사합니다.
4.2 객체의 속성 테스트
class MyClass:
@property
def my_property(self):
return 42
def test_my_property(mocker):
my_object = MyClass()
mock_property = mocker.PropertyMock(return_value=24)
mocker.patch.object(my_object.__class__, 'my_property', mock_property)
result = my_object.my_property
assert result == 24
-이 시나리오에서는 MyClass 인스턴스의 my_property 속성을 테스트하고 있습니다.
- 우리는 mocker.patch.object()를 사용하여 my_object 클래스의 my_property 속성을 24를 반환하는 모의 속성으로 바꿉니다.
- 그런 다음 my_object.my_property가 예상 값을 반환하는 것을 검사합니다.
4.3 타사 개체의 메서드 테스트
from third_party_module import ThirdPartyClass
def my_function():
obj = ThirdPartyClass()
return obj.my_method()
def test_my_function(mocker):
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(ThirdPartyClass, 'my_method', mock_method)
result = my_function()
assert result == 24
- 이 시나리오에서는 ThirdPartyClass의 인스턴스를 사용하고 my_method() 메서드를 호출하는 my_function() 함수를 테스트하고 있습니다.
- mocker.patch.object()를 사용하여 ThirdPartyClass의 my_method() 메서드를 24를 반환하는 모의 메서드로 바꿉니다.
- 그런 다음 my_function()이 예상 값을 반환하는 것을 검사합니다.
4.4 상속 계층에서 기본 클래스의 메서드 테스트
class MyBaseClass:
def my_method(self):
return 42
class MyClass(MyBaseClass):
pass
def test_my_method(mocker):
my_object = MyClass()
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(MyBaseClass, 'my_method', mock_method)
result = my_object.my_method()
assert result == 24
- 이 시나리오에서는 MyBaseClass에서 상속되는 MyClass 인스턴스의 my_method() 메서드를 테스트하고 있습니다.
- 우리는 mocker.patch.object()를 사용하여 MyBaseClass의 my_method() 메서드를 24를 반환하는 모의 메서드로 바꿉니다.
- 그런 다음 my_object.my_method()가 예상 값을 반환하는 것을 검사합니다.
4.5 믹스인 클래스의 메서드 테스트
class MyMixin:
def my_method(self):
return 42
class MyClass(MyMixin):
pass
def test_my_method(mocker):
my_object = MyClass()
mock_method = mocker.Mock(return_value=24)
mocker.patch.object(MyMixin, 'my_method', mock_method)
result = my_object.my_method()
assert result == 24
- 이 시나리오에서는 MyMixin 클래스를 믹스인으로 포함하는 MyClass 인스턴스의 my_method() 메서드를 테스트하고 있습니다.
- 우리는 mocker.patch.object()를 사용하여 MyMixin의 my_method() 메서드를 24를 반환하는 모의 메서드로 대체합니다.
- 그런 다음 my_object.my_method()가 예상 값을 반환하는 것을 검사합니다.
5. mock 주의할 점
- 코드가 변경되었거나, return의 값이 변경되었는데 이러한 부분이 target mock 코드에 반영되지 않았다면, 테스트는 원하는 결과대로 진행되지 않을 수 있다.
- 잘못된 target mock은 이를 참조하는 모든 테스트의 결과에 영향을 미친다.
reference:
* official
https://pytest-mock.readthedocs.io/en/latest/
* advanced
https://rumbarum.github.io/posts/pytest/pytest-mock/
https://daco2020.tistory.com/482
https://libertegrace.tistory.com/entry/TDD-Python-Testing-Framework2-Pytest-Unittest-mocking
https://4python.tistory.com/entry/TIL-Pytest-mock-server-%EA%B5%AC%EC%B6%95
https://github.com/nikhilkumarsingh/pytest-tut
* unittest.mock
https://docs.python.org/3/library/unittest.mock.html
'Django Web Framework > Django Pytest' 카테고리의 다른 글
Rest Framework를 위한 pytest 예시 코드 (0) | 2023.04.23 |
---|---|
[udemy] Real World Python Test Automation with Pytest 학습 (0) | 2023.04.14 |
pytest 확장 라이브러리 정리 (0) | 2023.04.09 |
[very academy] Pytest Mastery with Django (0) | 2023.04.02 |
@pytest.mark.django_db 와 db fixture, db 사용 법의 모든것 (0) | 2023.04.02 |
댓글