Skip to content

The `patches` test fixture

phlax edited this page Jan 16, 2022 · 3 revisions

The patches fixture provides a way of patching multiple objects in a python module.

Essentially it provides a context manager that provides nested context managers of objects patched with unittest.mock.patch.

The unittest.mock.patch contextmanager

Using the patch module from unittest.mock you can ordinarily do the following:

from unittest.mock import patch

from path.to.my.module import MyClass


def test_something1():

    with patch("path.to.my.module.MyClass.my_method1") as m_method1:
        MyClass().other_method_that_calls_my_method1()

If you wanted to patch multiple objects, properties, methods etc. you would then need to nest the context managers as follows:

from unittest.mock import patch

from path.to.my.module import MyClass


def test_something2():

    with patch("path.to.my.module.MyClass.my_method1") as m_method1:
        with patch("path.to.my.module.MyClass.my_method2") as m_method2:
            MyClass().other_method_that_calls_my_methods()

If you have a lot of objects to patch, the above method can get pretty complex with deeply nested context managers and a lot of repetition of the module path.

The patches contextmanager fixture

The patches fixture provides a way to do the above more simply and concisely.

Using patches the test_something2 test could be rewritten as follows:

from unittest.mock import patch

from path.to.my.module import MyClass


def test_something3(patches):
    patched = patches(
        "MyClass.method1",
        "MyClass.method2",
        prefix="path.to.my.module")

    with patched as (m_method1, m_method2):
        MyClass().other_method_that_calls_my_methods()

Providing kwargs to the patches contextmanager

Sometimes you need to patch an object with kwargs, for example if you are patching an object's @property you may wish to call unittest.mock.patch with new_callable=PropertyMock.

The args you call the patches function with represent the objects to be patched.

If an arg is a string the object will be patched without any kwargs.

If, on the other hand, the arg is a tuple containing two items, a string and a dict, the first item represents the object to be mocked, and the second the kwargs to patch the object with.

For example, to test a class property named property1 you might:

from unittest.mock import patch, PropertyMock

from path.to.my.module import MyClass


def test_something4(patches):
    patched = patches(
        ("MyClass.property1",               <-- mocked property
         dict(new_callable=PropertyMock)),
        "MyClass.method1",                  <-- mocked method
        prefix="path.to.my.module")

    with patched as (m_property1, m_method1):
        MyClass().method_that_calls_property1_and_method1()