Patch

In The Problem with Mocks
we discussed some of the potential problems with mock-based tests.
In this post we’ll discuss one of the most useful, and least problematic, ways
that mock is used in the Hypothesis tests - patch().

patch

patch is a custom pytest fixture that integrates
mock.patch
(a feature of the mock library that we haven’t discussed) with pytest
fixtures. You’ll see patch used very often in the Hypothesis tests.

patch is most often used when one of our modules imports and uses another
one of our own modules. patch replaces the other module with a MagicMock
object.

For example, h/views/api.py::create()
is the view function
that’s called when someone POSTs a new annotation to the
https://hypothes.is/api/annotations URL. It calls the storage module
to save the new annotation to the database. storage has its own tests and we
don’t want our test for create() to be accessing the real database,
so we want storage to be replaced with a mock object. This is done using a
simple patch-based fixture:

classTestCreate(object):deftest_it_creates_the_annotation_in_storage(self,storage):...# (Call the create() view to create an annotation)# Use the mock storage object to test that it would have saved the# annotation to storage.storage.create_annotation.assert_called_once_with(...)@pytest.fixturedefstorage(self,patch):# Replace h.views.api.storage with a mock object, and return the# mock object.returnpatch('h.views.api.storage')

The code under test, h/views/api.py, imports another module h/storage.py
like this:

from h import storage

In the tests above, the storage() fixture calls patch('h.views.api.storage')
which replaces the storage module in h/views/api.py with a mock object.
Since test_something_about_search() uses the storage() fixture, any views.py
code that this test calls will see a mock object in place of the real storage
module.

The storage() fixture happens to have the same name as the storage module
it replaces - this isn’t necessary, the storage() fixture could be called
anything and still replace the storage module - but giving patch fixtures the
same name as the thing they patch is a convention in the Hypothesis tests.

patch is an unusual fixture in that it’s almost always used by other fixtures,
like the storage fixture above, rather than by tests directly.

patch automatically takes care to do the mocking in the best way possible:

It uses autospec
so that, just like when creating a MagicMock with create_autospec(),
only those attributes that exist on the real object can be accessed or set
on the mock (recursively). Call signatures are also matched:
if code passes the wrong number of arguments, or a keyword argument that
doesn’t exist on the real method, to a mock object from the patch fixture
it’ll get a TypeError just as you would get from the real object.

patch also takes care of stopping the patch after the test method finishes,
before the next test is run, so that one test’s mock object doesn’t leak into
other tests.

It still isn’t perfect (see The Problem with Mocks
for the limitations of autospeccing)
but patch is one of the most useful and least
problematic applications of the mock library and you’ll see it used a lot in
the Hypothesis tests.