Skip to content

Usage

This library exposes a simple Matcher class and helper constructors via the mtch alias.

mtch.any

Matches any value.

assert mtch.any() == "anything"

mtch.type

Matches when the value is an instance of the provided type or types.

assert mtch.type(int) == 1
assert mtch.type(int, float) == 3.14

mtch.regex

Matches a string against a regular expression.

assert mtch.regex(r"\d+") == "123"

mtch.pred

Create a matcher from a custom predicate function.

def is_even(value: int) -> bool:
    return value % 2 == 0

assert mtch.pred(is_even) == 2
assert mtch.pred(lambda v: v > 0, "positive") == 3

mtch.eq

Creates a persistent matcher that remembers the first value it was compared with and requires subsequent comparisons to match that value.

m = mtch.eq()
assert m == "foo"
assert m == "foo"  # subsequent comparisons must match

Persistent matchers shine when you need to assert that the same value appears multiple times. Some handy scenarios include:

  • Repeated identifiers in nested objects – capture an ID once and ensure it matches everywhere else in the response.
  • Tokens shared across multi-step operations – store a session token from one request and verify it is reused in later calls.
  • Mock call sequences – check that the exact argument value is passed to different functions or multiple calls of the same mock.

For example:

from unittest.mock import Mock, call

user_id = mtch.eq()
assert {"id": 1, "child": {"id": 1}} == {"id": user_id, "child": {"id": user_id}}

token = mtch.eq()
assert {"token": "abc"} == {"token": token}
assert {"auth": "abc"} == {"auth": token}

mock = Mock()
mock(user_id)
mock(user_id)
assert mock.call_args_list == [call(user_id), call(user_id)]

bad = {"id": 1, "child": {"id": 2}}
assert bad != {"id": user_id, "child": {"id": user_id}}

Combining matchers

Matchers support logical operators:

  • & — logical AND
  • | — logical OR
  • ~ — logical NOT
number = mtch.type(int) | (mtch.type(str) & mtch.regex(r"\d+"))
assert number == 123
assert number == "456"

Matchers can also be used inside dictionaries or other containers for nested comparisons.

Nested data structures

Matchers can be mixed into lists or dictionaries to validate complex results without checking every value exactly.

matcher = {
    "id": mtch.type(int),
    "items": [
        {"value": mtch.regex(r"^x"), "meta": mtch.any()},
        mtch.type(float),
    ],
}
data = {"id": 1, "items": [{"value": "xyz", "meta": {}}, 1.5]}
assert matcher == data

Mock call assertions

Matcher instances work with unittest.mock and pytest helpers. They can be used to assert against call_args or call_args_list when exact values vary.

from unittest.mock import Mock, call

mock = Mock()
mock("foo", {"id": 1})

expected = call(mtch.regex("f.o"), {"id": mtch.type(int)})
assert mock.call_args == expected

mock = Mock()
mock(1)
mock("bar")

expected = [call(mtch.type(int)), call(mtch.regex("ba."))]
assert mock.call_args_list == expected