I just found out that you can use Python’s unittest.mock.ANY
to make assertions about
certain arguments in a mock call, without caring about the other arguments. This can be
handy if you want to test how a callable is called but only want to make assertions about
some arguments. Consider the following example:
# test_src.py
import random
import time
def fetch() -> list[float]:
# Simulate fetching data from a database.
time.sleep(2)
return [random.random() for _ in range(4)]
def add(w: float, x: float, y: float, z: float) -> float:
return w + x + y + z
def procss() -> float:
return add(*fetch())
Let’s say we only want to test the process
function. But process
ultimately depends on
the fetch
function, which has multiple side effects—it returns pseudo-random values and
waits for 2 seconds on a fictitious network call. Since we only care about process
, we’ll
mock the other two functions. Here’s how unittest.mock.ANY
can make life easier:
# test_src.py
from unittest.mock import patch, ANY
@patch("test_src.fetch", return_value=[1, 2, 3, 4])
@patch("test_src.add", return_value=42)
def test_process(mock_add, mock_fetch):
result = procss()
assert result == 42
mock_fetch.assert_called_once()
# Assert that the 'add' function was called with the correct
# arguments. Notice we only care about the first two arguments,
# so we've set the remaining ones to ANY.
mock_add.assert_called_once_with(1, 2, ANY, ANY)
While this is a simple example, I found ANY
to be quite useful while making assertions
about callables that accept multiple complex objects as parameters. Being able to ignore
some aruments while calling mock_callable.assert_called_with()
can make the tests more
tractable.
Under the hood, the implementation of ANY
is quite simple. It’s an instance of a class
that defines __eq__
and __ne__
in a way that comparing any value with ANY
will return
True
. Here’s the full implementation:
from __future__ import annotations
from typing import Any, Literal
class _ANY:
"A helper object that compares equal to everything."
def __eq__(self, other: Any) -> Literal[True]:
return True
def __ne__(self, other: Any) -> Literal[False]:
return False
def __repr__(self) -> str:
return "<ANY>"
ANY = _ANY()
It always returns True
whenever compared with some value:
In [1]: from unittest.mock import ANY
In [2]: ANY == 1
Out[2]: True
In [3]: ANY == "anything"
Out[3]: True
In [4]: ANY == True
Out[4]: True
In [5]: ANY == False
Out[5]: True
In [6]: ANY == None
Out[6]: True
Recent posts
- Explicit method overriding with @typing.override
- Quicker startup with module-level __getattr__
- Docker mount revisited
- Topological sort
- Writing a circuit breaker in Go
- Discovering direnv
- Notes on building event-driven systems
- Bash namerefs for dynamic variable referencing
- Behind the blog
- Shell redirection syntax soup