Skip to main content

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")
    # or
    # .then_call(some_callable)
    # or
    # .then_raise(SomeException())
)

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

Release files for pytest-when 2.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-when 2.3.0
File Size Uploaded
pytest_when-2.3.0.tar.gz 11.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-when 2.3.0
File Interpreter ABI Platform
pytest_when-2.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 20.6 kB

Release files / pytest_when-2.3.0.tar.gz

Download URL pytest_when-2.3.0.tar.gz
Size 11.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6edf6bd44508328b74bc0f7859e8128059ba015d2c924c0d3cc9cfd0231767a1
BLAKE2b-256 checksum
How to use checksums
30a12ae7c2637a5e6e961edb9a7a716e6fc1330dfc6439b50edde0eecce97a08
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.25.9 CPython/3.12.3 Linux/6.11.0-1018-azure

Release files / pytest_when-2.3.0-py3-none-any.whl

Download URL pytest_when-2.3.0-py3-none-any.whl
Size 9.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fc7e184f288416d7db4010d1cea667ea60f65a742559fdce90f26fd37d071065
BLAKE2b-256 checksum
How to use checksums
74048e032e875a3affb86c8293b8e41d74cd2ea214473051414525e684e2e2d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via pdm/2.25.9 CPython/3.12.3 Linux/6.11.0-1018-azure

Release history Release notifications | RSS feed

This release

2.3.0 This release

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.11

2 release files

1.0.10

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page