Skip to content

Getting started

Chainmock is a Python library designed to simplify and streamline the process of mocking, stubbing, and patching objects in your test code. It is built on top of the Python standard library unittest.mock module, providing a more intuitive and convenient interface for performing these tasks.

Because Chainmock uses Python standard library mocks under the hood, it is fully compatible with all the same features and functionality. In other words, everything that is possible with unittest.mock is also possible with Chainmock.

To get started with using Chainmock, import the mocker function:

from chainmock import mocker

mocker function is the main entrypoint to Chainmock. It provides different functionality depending on the arguments you pass to it. Supported functionalities are partial mocking (and spying), stubbing, and patching.

What you can do with Chainmock?

Note

These examples contain just a small part of Chainmock's functionality. For more details and examples, see the API reference.

Assert call counts

Assert that the method boil was called exactly once:

mocker(Teapot).mock("boil").called_once()

Assert that the method boil was called at least once but not more than twice:

mocker(teapot).mock("boil").call_count_at_least(1).call_count_at_most(2)

Assert that the method pour was called exactly three times:

mocker(teapot).mock("pour").call_count(3)

Assert call arguments

Assert that the method add_tea was called once with the argument puehr:

mocker(Teapot).mock("add_tea").called_once_with("puehr")

Assert that the method add_tea was called once with the argument oolong and once with the argument black:

from chainmock.mock import call
mocker(Teapot).mock("add_tea").has_calls([call("oolong"), call("black")])
Teapot().add_tea("oolong")
Teapot().add_tea("black")

Assert that the method add_tea was called at least once with the keyword argument loose=True:

mocker(Teapot).mock("add_tea").match_args_any_call(loose=True)
Teapot().add_tea("oolong", loose=True)

Mock return values and side effects

Mock the return value of method brew:

mocker(Teapot).mock("brew").return_value("mocked")
assert Teapot().brew() == "mocked"

Raise an exception when the method brew is called:

mocker(Teapot).mock("brew").side_effect(Exception("No tea!"))
Teapot().brew()
|
Traceback (most recent call last):
  ...
Exception: No tea!

Use a list to return a sequence of values:

mocker(teapot).mock("pour").side_effect([2, 1, Exception("empty")])
assert teapot.pour() == 2
assert teapot.pour() == 1
teapot.pour()
|
Traceback (most recent call last):
  ...
Exception: empty

Chaining assertions

Chainmock allows you to chain multiple assertions together, making it easy to test complex interactions in your code. For example, you can assert that a method was called with specific arguments and return a specific value:

mocker(Teapot).mock("add_tea").called_once_with("green").return_value("mocked")
assert Teapot().add_tea("green") == "mocked"

Another example, assert that the method add_tea was called at least once but not more than twice with the arguments green and black, and return a specific value:

mocked = mocker(Teapot).mock("add_tea")
mocked.call_count_at_least(1)
mocked.call_count_at_most(2)
mocked.has_calls([call("green"), call("black")])
mocked.return_value("mocked tea")
assert Teapot().add_tea("green") == "mocked tea"
assert Teapot().add_tea("black") == "mocked tea"

Utility objects

chainmock.mock module contains some re-exports from standard library unittest.mock module for convenience. It also contains utility objects that are helpful when asserting values in tests. Often you don't necessarily care about the exact value of something, but you might want to make sure that it is of a certain type.

unittest.mock module contains ANY object that can be compared to any other object and the comparison returns True. chainmock.mock contains the following additional objects: ANY_BOOL, ANY_BYTES, ANY_COMPLEX, ANY_DICT, ANY_FLOAT, ANY_INT, ANY_LIST, ANY_SET, ANY_STR, and ANY_TUPLE.

from chainmock import mock

assert {
    "bool": mock.ANY_BOOL,
    "bytes": mock.ANY_BYTES,
    "complex": mock.ANY_COMPLEX,
    "dict": mock.ANY_DICT,
    "float": mock.ANY_FLOAT,
    "int": mock.ANY_INT,
    "list": mock.ANY_LIST,
    "set": mock.ANY_SET,
    "string": mock.ANY_STR,
    "tuple": mock.ANY_TUPLE,
} == {
    "bool": True,
    "bytes": b"some_bytes",
    "complex": complex("1+2j"),
    "dict": {"nested": "value"},
    "float": 1.23,
    "int": 7983,
    "list": [1, 2, 3],
    "set": {"foo", "bar"},
    "string": "some_string",
    "tuple": (1, 2),
}

These objects are useful in argument matching when asserting calls to mocked methods. For example:

mocker(Teapot).mock("add_tea").called_once_with(mock.ANY_STR, mock.ANY_BOOL)
Teapot().add_tea("green", True)

Note

For more information, please see also API reference. It contains more examples and extensive documentation about every method and function available in Chainmock.