Python Mocking with unitest.mock 2
Internal
Mocking a Method
The general approach is to replace at runtime the real method instance associated with the class instance to be tested with a Mock
instance, configured to simulate various behaviors of the real method.
Assuming that our dependency to test with is SomeClass
, and this class has a some_method(nuance: str)
whose behavior we want to mock during testing, the initial implementation of the class and method could be:
class SomeClass:
def __init__(self, color: str):
self._color = color
def some_method(self, nuance: str) -> str:
return f'{nuance} {self._color}'.upper()
The normal behavior of the method some_method(nuance: str)
is reflected by:
c = SomeClass('blue')
assert c.some_method('dark') == 'DARK BLUE'
c._color = 'red'
assert c.some_method('light') == 'LIGHT RED'
We can mock the behavior of the some_method(nuance: str)
method in the following ways:
- We can return a constant value, regardless of the arguments the method is invoked with.
- We can raise an exception, regardless of the arguments the method is invoked with.
- We can replace the behavior of the method with arbitrary logic, that takes into account the arguments the method is called with.
Simulating a Particular Return Value Irrespective of the Arguments it was Called With
Configure Mock()
using the return_value
argument of its constructor so no matter how the mocked method is invoked, it will always return a constant value:
c = SomeClass('blue')
c.some_method = Mock(return_value='something completely arbitrary')
assert c.some_method('argument does not matter') == 'something completely arbitrary'
Irrespective of how the method is invoked in testing, the calling code will always get the value configured with return_value
on the mock.
This is a simple approach when we don't need flexible behavior depending on the arguments. For a more nuanced approach, see Arbitrary Behavior with Access to Invocation Arguments below.
Simulating Throwing an Exception Irrespective of the Arguments it was Called With
Configure Mock()
using the side_effect
argument of its constructor, by providing an exception **instance**. Once configured as such, the mocked method will always throw exception, no matter how it is invoked.
c = SomeClass('blue')
c.some_method = Mock(side_effect=ValueError('test'))
try:
c.some_method('argument does not matter')
except ValueError as e:
assert str(e) == 'test'