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)