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)