In the [pervious article], I used multiple patch
decorators to mock several functions. This time, I use Fixture
with the decorators to see how they work together.
Fixture
When I have a reusable object across multiple unit tests, I can define a fixture
and obtain it in the unit test function. I create one fixture this time to see how I can use it.
Structures and code
This is almost same as before, just modifying add_data
and main
method to take an argument, and
src/
├── my.py
├── my_modules/
│ ├── __init__.py
│ └── util.py
└── tests/
├── __init__.py
├── test_my.py
├── test_unit.py
└── conftest.py
my.py
import sys
from my_modules.util import util, get_data
def main(argv: list[str]):
data = get_data(argv)
return util(data)
if __name__ == '__main__':
main(sys.argv)
util.py
from datetime import datetime
def util(input: str) -> str:
input = add_time(input)
return f"util: {input}"
def add_time(input: str) -> str:
return f"{datetime.now()}: {input}"
def get_data(input: list[str]) -> str:
input = f"There are {len(input)} arguments"
return add_time(input)
Add fixture
I can define fixture in conftest.py that automatically recognize via test frameworks. I define a list_mock
fixture this time.
conftest.py
import pytest
@pytest.fixture
def list_mock():
return ["input1", "input2"]
Add unit test for get_data
Let's add or modify the unit test code for get_data.
@patch('my_modules.util.add_time')
def test_get_data(add_time, list_mock):
ct = datetime.now()
expected = f"{ct}: There are {len(list_mock)} arguments"
add_time.return_value = expected
result = get_data(list_mock)
assert expected == result
I patch
the add_time
function and receive the mock as the first argument of the test function. Then, I receive the list_mock
fixture as the second argument.
I can specify the fixture function name as argument name, then it is automatically passed to the function. Very easy!!
Update main unit tests
As the main
method also requires list[str]
argument, let's update them all. Just receive the list_mock
fixture as an argument and pass it to the main
function.
test_my.py
from my import main
from unittest.mock import patch, Mock
@patch("my.util", Mock(return_value="dummy"))
@patch("my.get_data", Mock(return_value="some data"))
def test_main(list_mock):
result = main(list_mock)
assert result =='dummy'
@patch("my.util")
@patch("my.get_data")
def test_main_util_called_with_expected_parameter(get_data_mock, util_mock, list_mock):
get_data_mock.return_value = 'some data'
util_mock.return_value = 'dummy'
result = main(list_mock)
assert result =='dummy'
util_mock.assert_any_call('some data')
def test_main_util_called_with_expected_parameter_with(list_mock):
with patch("my.util") as util_mock:
util_mock.return_value = 'dummy'
with patch("my.get_data") as get_data_mock:
get_data_mock.return_value = 'some data'
result = main(list_mock)
assert result =='dummy'
util_mock.assert_any_call('some data')
@patch("my.util", Mock(return_value="dummy"))
@patch("my.get_data")
def test_main_get_data_called(get_data_mock, list_mock):
get_data_mock.return_value = 'some data'
result = main(list_mock)
assert result =='dummy'
assert get_data_mock.called
Summary
I defined
as a fixture this time, but I can use any object including ``Mock`` object. When I write similar test objects in multiple unit test, we can move it to fixture.
See [the pytest fixtures official document](https://docs.pytest.org/en/7.1.x/how-to/fixtures.html) for more detail.
Top comments (0)