I wrote how to mock in the previous article. This time, I mock multiple functions in the test to see how I can handle them.
Structures and code
This is almost same as before, just adding one more function in util.py.
src/ ├── my.py ├── my_modules/ │ ├── __init__.py │ └── util.py └── tests/ ├── __init__.py ├── test_my.py └── test_unit.py
my.py
from my_modules.util import util, get_data def main(): data = get_data() return util(data) if __name__ == '__main__': main()
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() -> str: return "Return some data"
Add a unit test for main function
To test the main
method in the my.py
, I need to mock both util
and get_data
method. Let's do it.
Firstly, I added another patch
and specify return_value
. It doesn't change the test body though.
@patch("my.util", Mock(return_value="dummy")) @patch("my.get_data", Mock(return_value="some data")) def test_main(): result = main() assert result =='dummy'
Let's receive the mock objects as the arguments.
@patch("my.util") @patch("my.get_data") def test_main_util_called_with_expected_parameter(get_data_mock, util_mock): get_data_mock.return_value = 'some data' util_mock.return_value = 'dummy' result = main() assert result =='dummy' util_mock.assert_any_call('some data')
The interesting part is the order of argument. As you see, I can get the mock from bottom up order of the patch
decorators. I firstly though I can receive the mocks in the same order as the decorators, but I was wrong.
Finally, let's try with
statement.
def test_main_util_called_with_expected_parameter_with(): 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() assert result =='dummy' util_mock.assert_any_call('some data')
I am not 100% sure if this is the correct way to implement, but it works as expected anyway.
I just added one more example. This time, I specify the Mock object in the first decorator only. In this case, I can receive just one mock object as an argument.
@patch("my.util", Mock(return_value="dummy")) @patch("my.get_data") def test_main_get_data_called(get_data_mock): get_data_mock.return_value = 'some data' result = main() assert result =='dummy' assert get_data_mock.called
I check if get_data
function is called.
Summary
I understand when I get the mock object as the arguments. It's a bit confusing but once I understand, it's very useful.
Top comments (0)