Skip to main content

Utility which makes mocking more readable and controllable

Project description

PyPI Build Supported Python Versions Ruff

pytest-when

Inspired by mokito-scala, pytest-when provides a fixture for pytest to simplify the mocking of python objects:

Purpose

More readable than the full Given...When...Then pattern, pytest-when is meant for developers who want to test for behaviour, without any extra overhead. Enable the mock only for a specific argument's values to make code more readable.

Benefits

In this example, when you specify the first two arguments and any third argument, the attribute will be mocked,

(
    when(some_object, "attribute")
    .called_with(1, 2, when.markers.any)
    .then_return("attribute mocked")
)

Note that the .called_with method arguments are compared with the real callable signature. This gives additional protection against changing the real callable interface.

With when fixture, the following expression:

when(example_module, "some_normal_function").called_with(
    "a",
    1,
    kwarg1="b",
    kwarg2=2,
).then_return("Mocked")

is roughly equal to:

mock_only_for_calls = (
    call("a", 1, kwarg1="b", kwarg2=2),
    "Mocked",
)

def matches(
        call: Call,
        mocked_calls_registry: tuple[tuple[call, ReturnType], ...],
) -> bool:
    ...

def create_call(*args, **kwargs) -> Call:
    ...

def side_effect_callback(*args, **kwargs):
    if matches(create_call(*args, **kwargs), mock_only_for_calls):
        return mock_only_for_calls[1]
    return unittest.mock.DEFAULT


mocker.patch.object(
    example_module,
    "some_normal_function",
    autospec=True,
    side_effect=side_effect_callback,
)

where logic of matches and create_call is not trivial

Installation

Install the package into your development environment, from pypi, using pip, for example:

pip install pytest-when

Implementation

Onced installed, the when fixture will be available just like the rest of the pytest plugins. See the following example of how to use it:

# class which we're going to mock in the test
class Klass1:
    def some_method(
            self,
            arg1: str,
            arg2: int,
            *,
            kwarg1: str,
            kwarg2: str,
    ) -> str:
        return "some_method not mocked"


def test_should_properly_patch_calls(when):
    when(Klass1, "some_method").called_with(
        "a",
        when.markers.any,
        kwarg1="b",
        kwarg2=when.markers.any,
    ).then_return("some method mocked")

    assert (
            Klass1().some_method(
                "a",
                1,
                kwarg1="b",
                kwarg2="c",
            )
            == "some method mocked"
    )
    assert (
            Klass1().some_method(
                "not mocked param",
                1,
                kwarg1="b",
                kwarg2="c",
            )
            == "some method not mocked"
    )


# if you need to patch a function
def test_patch_a_function(when):
    when(example_module, "some_normal_function").called_with(
        "a",
        when.markers.any,
        kwarg1="b",
        kwarg2=when.markers.any,
    ).then_return("some_normal_function mocked")

    assert (
            example_module.some_normal_function(
                "a",
                1,
                kwarg1="b",
                kwarg2="c",
            )
            == "some_normal_function mocked"
    )
    assert (
            example_module.some_normal_function(
                "not mocked param",
                1,
                kwarg1="b",
                kwarg2="c",
            )
            == "some_normal_function not mocked"
    )

It is possible to use when with class methods and standalone functions (in this case cls parameter will become a python module).

You can patch the same object multiple times using different called_with parameters in a single test.

You can also patch multiple targets (cls, method)

See more examples at: test_integration

Setup for local developement

The project can be extended by cloning the repo and installing the PDM build tool So, the development environment requires:

  1. pdm https://pdm.fming.dev/latest/#installation
  2. python3.8 or greater
pdm install

To run tests and linters use:

make test
make lint

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_when-2.0.0.tar.gz (9.9 kB view details)

Uploaded Source

Built Distribution

pytest_when-2.0.0-py3-none-any.whl (7.4 kB view details)

Uploaded Python 3

File details

Details for the file pytest_when-2.0.0.tar.gz.

File metadata

  • Download URL: pytest_when-2.0.0.tar.gz
  • Upload date:
  • Size: 9.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.15.3 CPython/3.10.12 Linux/6.5.0-1021-azure

File hashes

Hashes for pytest_when-2.0.0.tar.gz
Algorithm Hash digest
SHA256 e6fc7be98b3f63c587cd9044da310c3ba63109ac48c2debacf2426cbe64e0c09
MD5 73357411915e09908a1137ee78fc8853
BLAKE2b-256 9bbbe62aa8a23a68bcf67886b384c719bb066afb0e0e43fcea2a84173b242c9d

See more details on using hashes here.

File details

Details for the file pytest_when-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_when-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 7.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.15.3 CPython/3.10.12 Linux/6.5.0-1021-azure

File hashes

Hashes for pytest_when-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 15a87e2d3e92673c24a49e5e607c957569260cbb64a00f9384352f81a914492b
MD5 f1ce1e7a0bacb3a90537b07e5e97688f
BLAKE2b-256 db4b1721bfc49bf840fbb97262d3fdda763da79c56e7bb0b7926260c98325d96

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page