(For those who got here curious about one mock returning different distinct values, see my use of side_effect
at the end of my answer below.)
I'll start with the first statement in your question:
I'm trying to mock a library (matplotlib
for what it's worth)
If you are able to mock this library properly, you shouldn't have to worry about the behavior of any the subfunctions - as they will all be mocked and you can determine how the mocked functions should behave. How I have achieved similar behavior is by using patch
- specifically, patch.object
should help:
import mock
mock_matplotlib = mock.MagicMock()
mock_matplotlib.return_value = whatever_mock_behavior_you_want
with mock.patch.object(my_app, "matplotlib", mock_matplotlib):
# Call the function you are testing which uses matplotlib
You now have the freedom to substitute whatever you'd like in for mock_matplotlib
so that it behaves in the way you seek... (i.e. mock_matplotlib.return_value = mock_tuple
) Let's explore some of those possibilities.
You are correct when you mentioned that MagicMock
always returns a MagicMock
for anything called on it - however, with that statement of a, b = foo()
, Python is expecting to find a tuple and the error is triggering before the MagicMock
can create the default single MagicMock
return value, so the error says that it finds 0
values to work with.
Your solution of creating a tuple of MagicMock
s could work a number of different ways - it is not clear from your question whether assigning values with a, b =
is more important to you or not. If it is then you could do this:
import mock
foo = mock.MagicMock()
a, b = (mock.MagicMock(), mock.MagicMock())
or this:
a, b = (foo(), foo())
Or, if you are more interested in having one function call fill those values for you, as mentioned, you could assign a return_value
like this:
foo = mock.MagicMock()
foo.return_value = (mock.MagicMock(), mock.MagicMock())
a, b = foo()
or even like this:
foo = mock.MagicMock(return_value=(mock.MagicMock(), mock.MagicMock()))
a, b = foo()
For those who are curious about this comment, the OP is correct that this is different from returning a different value each time the MagicMock
is called. That would use the side_effect
option - however, even that could be used to achieve the OP's goal - albeit in a roundabout way:
mock_1 = mock.MagicMock()
mock_2 = mock.MagicMock()
foo = mock.MagicMock(side_effect=[mock_1, mock_2])
a, b = (foo(), foo())
In that last example, mock_1
is assigned to a
, and mock_2
is assigned to b
.