equals: Enhanced Equality Testing for Python

equals
is a Python library that provides flexible equality assertions for testing. Think of it as a more powerful version of Mock.Any
that gives you fine-grained control over how objects are compared.
Why Use equals?
When writing tests with mocks and stubs, exact matching can make tests brittle and hard to maintain. Often, you care about certain properties of an object but not others. For example:
- In integration tests, timestamps or UUIDs might change between test runs
- When mocking API responses, you may only care about specific fields
- Testing event handlers where message format may evolve over time
- Verifying call arguments where some data is non-deterministic
equals
helps you write more maintainable tests by focusing on the properties that matter for your test cases, making your test suite more robust and easier to maintain.
The equals
library provides a rich set of fuzzy matching capabilities that go beyond exact equality comparisons. Each matcher type (string, number, dictionary, etc.) implements its own set of flexible matching rules, allowing you to write tests that focus on the important aspects of your data while ignoring irrelevant details. These matchers can be combined and chained to create sophisticated matching patterns that make your tests both robust and maintainable.
Quick Examples
The equals
library enables flexible matching in tests through partial equality checks:
- Match dictionaries by key presence or content
- Match strings by pattern or content
- Chain multiple conditions for complex matching
With Mock and pytest
import pytest
from unittest.mock import Mock
from equals import any_dict, any_string
def test_mock_multiple_calls():
mock_service = Mock()
mock_service.update({'name': 'bob'})
mock_service.update({'name': 'alice'})
assert mock_service.update.call_count == 2
mock_service.update.assert_any_call(any_dict.containing(name=any_string))
With dobles and pytest
import pytest
from dobles import expect
from equals import any_string
class UserService:
def validate_email(self, email: str) -> bool:
return '@' in email
@pytest.fixture
def user_service():
return UserService()
def test_email_validation(user_service):
expect(user_service).validate_email.with_args(
any_string.containing('@').and_containing('.')
).and_return(True)
assert user_service.validate_email('test@example.com')
Fuzzy Matching Capabilities
String Matching
from equals import any_string
assert any_string.containing('abc') == '123 abc 456'
assert any_string.starting_with('abc') == 'abcdef'
assert any_string.ending_with('abc') == '123abc'
assert any_string.matching('^abc$') == 'abc'
Number Comparisons
from equals import any_number
assert any_number.less_than(5) == 4
assert any_number.between(1, 3) == 2
assert any_number.greater_than(4) == 5
Dictionary Validation
from equals import any_dict
assert any_dict.containing(foo='bar') == {'foo': 'bar', 'other': 'value'}
assert any_dict.not_containing(foo=5) == {'foo': 3, 'bar': 5}
Iterable Operations
from equals import any_iterable
assert any_iterable.containing(1, 2) == [1, 2, 3]
assert any_iterable.containing_only(1, 2) == [2, 1]
assert any_iterable.with_length(2) == [3, 4]
Object Matching
from equals import anything, instance_of
assert anything == 'any value'
assert instance_of(dict) == {}
assert anything.with_attrs(foo='bar') == obj
Installation
pip install equals
Development
The source code is available on GitHub.
Getting Started
make bootstrap
make test
make docs
Documentation
Full documentation is available at equals.readthedocs.org.
License
MIT License