23

I'm having trouble understanding what's happening in some test code. It looks like this:

import pytest
from unittest.mock import MagicMock
from my_module import MyClass

confusing_mock = MagicMock(
    return_value=b"",
    side_effect=[
        ConnectionError(),
        b"another_return_value?",
        b"another_another_return_value?"
    ])

mocked_class = MyClass()
monkeypatch.setattr(mocked_class, "method_to_call_thrice", confusing_mock)

I know that:

  • side_effect is a function to be called whenever the mock is called
  • but if side_effect is an iterable, then "each call to the mock will return the next value from the iterable" (thanks pytest docs)
  • the docs also say that if the function passed to side_effect returns DEFAULT, then the mock will return it's normal value from return_value

But here's what I don't get:

  • What happens when I provide both a list of side effects and a return value?
  • What should I expect to see on each call of MyClass.method_to_call_thrice?
tessafyi
  • 2,273
  • 2
  • 19
  • 28
  • 1
    The question "[unittest.Mock - Combining return_value and side_effect](https://stackoverflow.com/questions/47176726/unittest-mock-combining-return-value-and-side-effect)" seems related, but I'm still not quite getting it. – tessafyi May 17 '19 at 17:42
  • 2
    You could try with a simple return valeu and a simple side effect and see what happens? – Tom Dalton May 17 '19 at 17:46

1 Answers1

34

side_effect is used. A list value can contain mock.DEFAULT, and a function can return mock.DEFAULT, to indicate that the value of the return_value attribute be used.

>>> import unittest.mock
>>> m = unittest.mock.Mock(return_value="foo",
...                        side_effect=[1, 2, unittest.mock.DEFAULT, 4, 5])
>>> m()
1
>>> m()
2
>>> m()
'foo'
>>> m()
4
>>> m()
5
>>> unittest.mock.Mock(return_value="foo",
...                    side_effect=lambda: unittest.mock.DEFAULT)()
'foo'
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 2
    To remove an already present `side_effect`, you can also set it to `None` (per the docs [here](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock)) – Intrastellar Explorer Sep 16 '21 at 02:11