I have this class Waterfall
:
class Waterfall:
def height_in_feet(self):
return requests.get("https://nicaragua.waterfall.com/height").content
It's making web service calls to Nicaragua all the time, requiring me to be online whenever I run my tests:
import unittest
class ExploringMocking(unittest.TestCase):
def test_using_the_real_class(self):
waterfall = Waterfall()
assert waterfall.height_in_feet() == 164
For this reason, I'd like to mock the Waterfall
class (as it has so
many methods and I want the method signatures to all be correct, I use
unittest.mock.create_autospec
) and provide my own implementation of
the height_in_feet()
method:
import types
import unittest.mock
class ExploringMocking(unittest.TestCase):
def test_mocking_a_class(self):
waterfall = unittest.mock.create_autospec(Waterfall)
waterfall.height_in_feet = types.MethodType(
self.my_height_func, self
)
assert waterfall.height_in_feet() == 10
def my_height_func(self, ref):
return 10
The essential bit here is the types.MethodType
which is what you
need to do when overriding the method on a class instance. Just
doing waterfall.hight_in_feet = self.my_height_func
will not cut it.
With this in place, I can implement any logic I want in
my_height_func
which will get executed whenever
Waterfall#height_in_feet()
would have been.
If you strive to see the point of this, consider the case where you
have a method that uses an instance Waterfall
. When passing in an
instance of Waterfall
, we want to control what the method is
returning depending on the input. Imagine that we have this silly
method:
def get_colour_of_waterfall(waterfall: Waterfall) -> str:
if waterfall.height_in_feet() > 10:
return "blue"
else:
return "true"
By using the original implementation of Waterfall
, we not only have
to be online all the time (and thus writing an integration test instead
of a unit test), but even more importantly, we cannot test what this
get_colour_of_waterfall
method returns given different situations
and return values from the Nicaragua web service.
Overriding the Waterfall#height_in_feet()
method solves both of
these problems. With it, we not only can write a pure unit test (no
need to interact with the other system, no need to be online) and we
can easily simulate different values, or exceptions, returned from
Waterfall#height_in_feet()
.
Happy testing!