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-1.1.5.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_when-1.1.5-py3-none-any.whl (7.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pytest_when-1.1.5.tar.gz
  • Upload date:
  • Size: 9.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.12.4 CPython/3.10.12

File hashes

Hashes for pytest_when-1.1.5.tar.gz
Algorithm Hash digest
SHA256 234669b140dd54b45ebdeb8cd1fbcbf4b2a6921104ef2774f5f59926b0e95abf
MD5 f887ccfc76aaa8bd1d1c94008987477b
BLAKE2b-256 ee5aa34f40f247aa4192c889e2fc320ef8078f0a4a0c7ccf3128ea2e04ec3391

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pytest_when-1.1.5-py3-none-any.whl
  • Upload date:
  • Size: 7.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.12.4 CPython/3.10.12

File hashes

Hashes for pytest_when-1.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 c122c5f232ce9cb90db800a29bc122175b3fa08994d32b4301b1430023288cc9
MD5 2a08699b67bdb147410c12c7d69917a2
BLAKE2b-256 79cd870d94585a28887bdb38072e5ac00ffc30abe288882d5e077985433b39b1

See more details on using hashes here.

Supported by

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