Callee: Argument Matchers for unittest.mock
Callee provides a set of powerful argument matchers designed to extend the capabilities of Python's `unittest.mock` library. It allows developers to write more expressive and robust assertions for mocked calls, enabling checks for patterns, types, ranges, and more, beyond simple value equality. The current version is 0.3.1, with a stable but infrequent release cadence.
Common errors
-
TypeError: argument of type 'Regex' is not iterable
cause Attempting to use a `callee` matcher directly in a comparison or context where `unittest.mock`'s evaluation logic is not present, such as `assert 'some_string' == Regex(r'some.*')`.fixEnsure `callee` matchers are always passed as arguments to `unittest.mock` assertion methods, like `mock_obj.assert_called_with(Regex(r'some.*'))`. -
AssertionError: Expected call: call(<callee.matchers.Eq object at 0x...>) Actual call: call(6)
cause This `AssertionError` from `unittest.mock` indicates that an expected `callee` matcher (e.g., `Eq(5)`) did not match the actual argument passed to the mock (e.g., `6`). The error message shows the matcher object because it represents the expectation that failed.fixReview the actual values being passed to the mock during the test run and compare them against the conditions defined by your `callee` matchers. Adjust either the test data or the matcher's expectation to align. -
NameError: name 'Any' is not defined
cause The `Any` matcher (or any other `callee` matcher) was used without being correctly imported from the `callee` library.fixAdd the necessary import statement at the top of your file: `from callee import Any` (or other specific matchers).
Warnings
- breaking Version 0.3.0 introduced breaking changes: The `class_of` matcher was removed (use `InstanceOf` instead). The `call_has_args` and `call_has_kwargs` methods were renamed to `call_args_are` and `call_kwargs_are` respectively.
- gotcha `callee` matchers are primarily designed for use within `unittest.mock`'s assertion methods (e.g., `assert_called_with`, `assert_has_calls`, checking `call_args`). They are not general-purpose assertion objects and should not be used directly in standard Python `assert` statements (e.g., `assert 'foo' == Regex(r'foo')` will likely fail or behave unexpectedly) as they rely on `unittest.mock`'s internal comparison logic.
- gotcha Be mindful of the distinction between `callee.Any` and `unittest.mock.ANY`. While both signify 'any argument', `callee.Any` offers more advanced capabilities, such as specifying a type (e.g., `Any(int)`) or other conditions, making it more powerful for precise matching. `unittest.mock.ANY` is a simple singleton for any value.
Install
-
pip install callee
Imports
- Any
from unittest.mock import ANY
from callee import Any
- Eq
from callee import Eq
- Regex
from callee import Regex
- InstanceOf
from callee import InstanceOf
- Not
from callee import Not
Quickstart
from unittest import mock
from callee import Eq, Regex, Any
class MyService:
def process_data(self, data_id, name):
# Imagine this does something with data_id and name
return f"Processed {name} with ID {data_id}"
def external_function(service: MyService):
# This function calls methods on the provided service
service.process_data(123, "test_name_abc")
service.process_data(456, "another_name_xyz")
# Create a mock for MyService
mock_service = mock.Mock(spec=MyService)
# Call the function that uses the service, injecting our mock
external_function(mock_service)
# Assert that process_data was called with specific matchers
# This checks the first call
mock_service.process_data.assert_has_calls([
mock.call(Eq(123), Regex(r'^test_name_.*$')),
mock.call(Any(int), Any(str)) # Any integer, any string for the second call
])
print("Assertions successful!")