Faking Values

There are multiple reasons we may wish to patch values in our tests, including:

  • Simulating external service responses
  • Controlling return values
  • Error handling
  • Speeding up tests

For example, test_registration_days_remaining_before_submission relies on calculating the difference in days between datetime.now().date() and Registration.audit_submission_date.

Of course, datetime.now().date() will always change, leading to the tests breaking at some eventual future time point.

Instead, we can temporarily patch the return value of datetime.now().date(), only within the scope of this test, with the @patch.object() decorator, using the following syntax:

from unittest.mock import patch

@patch.object(CLASS_NAME_TO_PATCH, 'CLASS_METHOD_NAME_TO_PATCH', return_value=VALUE_TO_RETURN)

In action, this looks like this:

test_registration.py
from unittest.mock import patch

...

@patch.object(Registration, 'get_current_date', return_value=date(2022, 11, 30))
@pytest.mark.django_db
def test_registration_days_remaining_before_submission(
    mocked_get_current_date,
    example_fresh_registration,
):

    ...

We assign the return value of Registration.get_current_date to always be 2022-11-30.

Using the @patch.object decorator also passes in the patched object into the test function as the FIRST parameter, which can be called anything - in this case, we call it mocked_get_current_date. Other fixtures required for the test are passed after.

If you use multiple @patch.object decorators, patched objects are passed in the same order decorators are evaluated:

test_registration.py
from unittest.mock import patch

@patch.object(OBJECT1, 'method_to_patch', return_value=1)
@patch.object(OBJECT2, 'method_to_patch', return_value=3)
@patch.object(OBJECT3, 'method_to_patch', return_value=3)
def test_my_test(
    patched_object_3,
    patched_object_2,
    patched_object_1,
):
    ...