Python Mock Multiple Calls with Different Results
What is a mock?
When testing code, it can be useful to mock function calls that rely on external dependencies, such as another module or an external service. By mocking these function calls, you can replace the actual implementation of the function with one that you have created specifically for the test. This allows you to isolate the code you are testing and ensure that the test results are reliable, even when the external dependencies may not be functioning properly. Additionally, mocking function calls can be particularly useful when your code relies heavily on these external dependencies, as it allows you to more easily test the functionality of your code without being affected by potential issues with the dependencies.
For example, if you have a function that sends an HTTP request to a server, you could use mocking to test how your code behaves when it receives a response from the server without actually making an HTTP request. You need to be exra careful here though, because if you mock the wrong request/response it means that although the tests will be green ✅ the actual implementation will be wrong and when is time to run this in production it will break.
I'll do a more in-depth blog on how to test and mock functions in python so make sure to subscribe!
The problem
Today, I was working on a refactoring and an interesting problem surfaced. The issue was that the actual implementation of a function returned a different result on each call. When I refactored the code, I could't use the actual implementation anymore so I ended up writing a mock but how could you call a mocked function multiple times and each time it would return a different result for each successive call?
The solution
To mock multiple calls with different results, you can use the side_effect
attribute of a mock object. This attribute can be set to an iterator (like a python list), which will be used in the order they are specified whenever the mock object is called. Here is an example:
from unittest.mock import Mock
mock = Mock(side_effect = [1, 2, 3])
assert mock() == 1 # True
assert mock() == 2 # True
assert mock() == 3 # True
assert mock() == 4 # StopIteration
You can see here, that each time we call the mock a different result is given because each time a value from the list is consumed. In the example above, I am using pytest that's why the assertions are written like that.
Here's the same issue answered in stack overflow that you can check out
A different between the side_effect
attribute and the return_value
attribute is that the iterator passed to the side_effect
will get consumed. If there are no other elements in the iterator the mock object will raise a StopIteration
exception. In contrast the return_value
attribute can be used as many times as we won't since every time that the mocked function is called the result will be always the same when the return_value
is used.
The end
Congratulations! You made it to the end. It was an easy one :)
If you like the content here please consider subscribing. No spam ever!
If you have any comments or feedback feel free to reach me @costapiy