Django Web Framework/Django Pytest

pytest-mock의 사용법

bluebamus 2023. 4. 14.

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/

 

pytest-mock documentation

Next Usage

pytest-mock.readthedocs.io

* advanced

https://rumbarum.github.io/posts/pytest/pytest-mock/

 

pytest-mock sample들 - 개발세발개발

required pip install pytest pytest-mock pytest의 플러그인인 pytest-mock을 통해 mocking을 해보도록 하겠습니다. 폴더구조와 구성은 다음 과 같습니다. class SomethingExport: val1 = 1000 val2 = 1000 val3 = 1000 def val(self): re

rumbarum.github.io

https://daco2020.tistory.com/482

 

pytest _ mock 사용하여 테스트 코드 작성하기

mock이란? 쉽게 말해 ‘가짜’ 데이터라고 이해하면 된다. 예를 들어 테스트 코드를 작성할 때, 실제 모듈과 유사하게 동작하는 가짜 데이터를 만들어 사용할 수 있다. ‘가짜’ 데이터인 ‘mock’

daco2020.tistory.com

https://libertegrace.tistory.com/entry/TDD-Python-Testing-Framework2-Pytest-Unittest-mocking

 

[TDD] Python Testing Framework2 - Pytest, Unittest - mocking

1. Mock mocking이 필요한 경우를 예시로 들고, 해당 mocking을 Unittest로 구현할 때와 Pytest로 구현할 때를 나누어서 살펴보겠습니다. 1) Random result mocking import random import requests def roll_dice(); print("rolling...

libertegrace.tistory.com

https://medium.com/oheadline/%EC%98%A4%EB%8A%98%EC%9D%98-%ED%97%A4%EB%93%9C%EB%9D%BC%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EA%B8%B0-2-mock-45ca0f239b08

 

[오늘의 헤드라인] 테스트 코드 작성기(2) — Mock

안녕하세요. 오늘의 헤드라인 서버팀의 소프트웨어 엔지니어 Jun입니다. 오늘의 헤드라인에서 ‘python 기반 테스트 코드를 작성한 과정과 고민’을 2편을 공유하고자 합니다.

medium.com

https://4python.tistory.com/entry/TIL-Pytest-mock-server-%EA%B5%AC%EC%B6%95

 

[TIL] Pytest - mock server 구축

TestCode 지금 있는 속한 팀에 있기 전, 바로 직전에는 QA팀에서 Tool을 만들었다. 그때도 동일하게 테스트코드를 작성을 하였지만, 이 글에서 이야기하는 테스트코드와는 결이 달랐다. 개발자가 자

4python.tistory.com

https://github.com/nikhilkumarsingh/pytest-tut

 

GitHub - nikhilkumarsingh/pytest-tut: Unit Testing in Python with pytest

Unit Testing in Python with pytest. Contribute to nikhilkumarsingh/pytest-tut development by creating an account on GitHub.

github.com

 

 

* unittest.mock

https://docs.python.org/3/library/unittest.mock.html

 

unittest.mock — mock object library

Source code: Lib/unittest/mock.py unittest.mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they hav...

docs.python.org

 

댓글