Fluent assertion library for Python with composable matchers, structural matching, and full type safety
Project description
Fluent assertion library for Python with composable matchers, structural matching, and full type safety.
A modern, batteries-included fork of assertpy.
Quick start
pip install assertpy2 # drop-in replacement for assertpy, just change the import
from assertpy2 import assert_that
def test_user():
user = {"name": "Alice", "age": 30, "roles": ["viewer", "editor"]}
assert_that(user).contains_key("name", "age")
assert_that(user["age"]).is_between(18, 120)
assert_that(user["roles"]).contains("viewer").does_not_contain("admin")
assert_that(user).has_name("Alice")
Browse the full documentation for every assertion, matcher, and integration.
Why fluent assertions?
A fluent chain reads as one intent and replaces several bare asserts - and your IDE offers only the methods that fit the value's type:
# bare - three statements, no autocomplete help
assert isinstance(items, list)
assert len(items) == 3
assert "admin" in items
# assertpy2 - one chain, type-aware autocomplete
assert_that(items).is_type_of(list).is_length(3).contains("admin")
The real difference shows up when a test fails. Here a nested response has two wrong
fields. Plain assert dumps both structures and leaves you to find them:
assert response == expected
E AssertionError: assert {'id': 1, ...} == {'id': 1, ...}
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'user': {'name': 'Alice', 'role': 'superadmin'}} != {'user': {'name': 'Alice', 'role': 'admin'}}
E {'status': 'active'} != {'status': 'disabled'}
assertpy2 reports the exact path to every difference, in color:
assert_that(response).is_equal_to(expected)
Recursive diffs work for dicts, dataclasses, namedtuples, and Pydantic models.
For responses with dynamic fields (IDs, timestamps), validate a subset with
matches_structure() instead of exact equality.
The same path-level treatment for dicts, lists, sets, and matcher predicates:
Type-aware autocomplete
assert_that() uses @overload to return type-specific Protocols.
Your IDE shows only methods relevant to the value you're testing, not all 100+:
assert_that("hello").→ string methods:starts_with,matches,is_alpha, ...assert_that(42).→ numeric methods:is_positive,is_between,is_close_to, ...assert_that(Path("/tmp")).→ path methods:exists,is_file,is_readable, ...assert_that(my_dict).→ dict methods:contains_key,contains_entry,has_json_path, ...assert_that(b"\x89PNG").→ bytes methods:starts_with_bytes,is_valid_utf8,decoded_as, ...
9 type-specific Protocols instead of one Any. Works in PyCharm, VS Code, and any LSP-compatible editor.
See the Type Safety guide for the full walkthrough.
Features
Fluent API
- Composable matchers:
match.greater_than(5),match.is_uuid(), combine with&,|,~. Also work with plainassert ==. - Structural matching:
matches_structure()for declarative dict/API response validation, reporting the exact path to each mismatch on failure. - Recursive field assertions:
all_fields_satisfy()/has_no_none_fields()apply a predicate to every leaf of an object graph, reporting the exact path. - Universal negation:
.not_inverts any assertion without dedicatedis_not_*methods. - Collection pipeline:
filtered_on(),mapped(),flat_mapped(),first(),last(),element(),single(). - Positional & pairwise checks:
satisfies_exactly(),zip_satisfies(),contains_only_once(),has_same_size_as(). - Fluent chaining: write assertions as readable one-liners that chain naturally.
Built-in types
- Strings, numbers, lists, tuples, sets, dicts, dates, booleans, objects, bytes, files, exceptions.
- Bytes assertions:
is_valid_utf8(),starts_with_bytes(),is_hex_equal_to(),decoded_as()forbytes/bytearray. - Dynamic assertions:
has_<name>()for any attribute, property, or zero-argument method. - Dict comparison:
is_equal_to()withignoreandincludefor selective key/field matching (dicts, dataclasses, namedtuples, Pydantic models, attrs, plain objects), including by regex or type. - Recursive comparison:
is_equal_to()withtolerance(absolute float tolerance at any depth) orcomparators(per-type / per-field predicates) for nested structures. - Extracting: flatten collections on attributes with
filterandsortsupport.
Testing
- Soft assertions: thread-safe, async-safe via
contextvars. Group errors withsa.group(), or useassert_all(). - Async assertions:
eventually()with polling/retry for eventual consistency. - Structured errors:
AssertionFailurewith.actual,.expected,.diffattributes. - Rich pytest diffs: recursive structural diffs for lists, sets, strings, dicts, dataclasses, namedtuples, Pydantic models, and matcher-based assertions (
matches_structure(),satisfies(),each()). Circular reference protection. - Snapshot testing: store and compare data structures in JSON format.
- Property-based tested: comparison, selective-diff, matcher algebra, and collection logic are checked with Hypothesis against reference semantics, on top of 100% branch coverage.
Type safety
- Type-aware autocomplete: 9 Protocols, IDE shows only relevant methods per type.
- py.typed:
Selfreturn types, PEP 561 compliant (PEP 561).
Extensibility
- Custom matchers:
register_matcher()for domain-specific matchers, composable with&,|,~. - Regex group extraction:
extracting_group()andmatches_with_groups()for regex captures. - Extensions:
add_extension()for custom assertion methods.
See the full documentation for all assertion methods, examples, and advanced features.
Integrations
Optional adapters, each its own extra; full configuration and examples are in the Integrations guide.
- Allure (
pip install assertpy2[allure]): the pytest plugin auto-attaches structured diff and actual/expected data to Allure reports, in three configurable modes. - Behave (
pip install assertpy2[behave]): ready-made parameter types (PositiveInt,NonEmptyString, ...) for step definitions like{age:PositiveInt}. - JSON (
pip install assertpy2[json]): JSONPath navigation (at_json_path(),has_json_path()) and JSON Schema validation (matches_json_schema()). - Data frames (
pip install assertpy2[pandas]/[polars]/[numpy]): fluent equality for pandas/polars frames and numpy arrays, carrying each library's own diff.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file assertpy2-2.12.0.tar.gz.
File metadata
- Download URL: assertpy2-2.12.0.tar.gz
- Upload date:
- Size: 447.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0b4f38fac50eaea8d2199c706c77364623e817ec84eff6a41dacbc888e0ccdd
|
|
| MD5 |
09fc52bed9211a8ab96818fe77c96718
|
|
| BLAKE2b-256 |
d3f85ed014afc9e31b8d2aefa456d935c52c95620118eec1e9a9f0896acc177e
|
Provenance
The following attestation bundles were made for assertpy2-2.12.0.tar.gz:
Publisher:
publish.yml on Solganis/assertpy2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assertpy2-2.12.0.tar.gz -
Subject digest:
c0b4f38fac50eaea8d2199c706c77364623e817ec84eff6a41dacbc888e0ccdd - Sigstore transparency entry: 2011796545
- Sigstore integration time:
-
Permalink:
Solganis/assertpy2@003a1691a6c6a38c6681d78295f59cacd724fb06 -
Branch / Tag:
refs/tags/v2.12.0 - Owner: https://github.com/Solganis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@003a1691a6c6a38c6681d78295f59cacd724fb06 -
Trigger Event:
release
-
Statement type:
File details
Details for the file assertpy2-2.12.0-py3-none-any.whl.
File metadata
- Download URL: assertpy2-2.12.0-py3-none-any.whl
- Upload date:
- Size: 80.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ac8cb18559191176f7137053af5f16390c378cc620031e61a4deb41dc3ece53
|
|
| MD5 |
891c7b299b5e25fd76b9f75692875a55
|
|
| BLAKE2b-256 |
9a505ed74c479e5567ae16e8b71ab41ba212dfc42a7174beb5f1598e243350e3
|
Provenance
The following attestation bundles were made for assertpy2-2.12.0-py3-none-any.whl:
Publisher:
publish.yml on Solganis/assertpy2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assertpy2-2.12.0-py3-none-any.whl -
Subject digest:
8ac8cb18559191176f7137053af5f16390c378cc620031e61a4deb41dc3ece53 - Sigstore transparency entry: 2011796636
- Sigstore integration time:
-
Permalink:
Solganis/assertpy2@003a1691a6c6a38c6681d78295f59cacd724fb06 -
Branch / Tag:
refs/tags/v2.12.0 - Owner: https://github.com/Solganis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@003a1691a6c6a38c6681d78295f59cacd724fb06 -
Trigger Event:
release
-
Statement type: