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)