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, attrs, and Pydantic models.
For responses with dynamic fields (IDs, timestamps), validate a subset with
matches_structure() instead of exact equality.
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. - Universal negation:
.not_inverts any assertion without dedicatedis_not_*methods. - Collection pipeline:
filtered_on(),mapped(),flat_mapped(),first(),last(),element(),single(). - 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. - JSON assertions: JSONPath navigation and JSON Schema validation.
pip install assertpy2[json]. - 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). - 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.
Integrations
- Allure: auto-attach structured diff and actual/expected data to reports.
pip install assertpy2[allure]. - Behave: ready-made parameter types for step definitions.
pip install assertpy2[behave].
See the full documentation for all assertion methods, examples, and advanced features.
Integrations
Allure
When allure-pytest is installed, the pytest plugin auto-attaches structured failure data to Allure reports as JSON attachments.
pip install assertpy2[allure]
Three modes controlled via pytest.ini (or pyproject.toml):
| Mode | What is attached |
|---|---|
diff (default) |
Structured Diff JSON (path-level breakdown) |
full |
Structured Diff + actual/expected JSON |
off |
Nothing |
# pyproject.toml
[tool.pytest.ini_options]
assertpy2_allure = "full"
Behave
Ready-made parameter types for Behave step definitions:
pip install assertpy2[behave]
# in environment.py or steps/conftest.py
from assertpy2.behave_matchers import register_assertpy_types
register_assertpy_types()
Then use in step definitions:
@given('a user aged {age:PositiveInt}')
def step_impl(context, age):
context.age = age # already validated as int > 0
Available types: PositiveInt, NonNegativeInt, PositiveFloat, NonEmptyString, BoolLike.
See the Integrations guide for attachment modes, configuration, and full examples.
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.9.1.tar.gz.
File metadata
- Download URL: assertpy2-2.9.1.tar.gz
- Upload date:
- Size: 336.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fb5b9b7dfa05b29f376918460385b1262548bd1087994d739193ef2b2907e95
|
|
| MD5 |
749969108d5fe0b681eb54d48fc76081
|
|
| BLAKE2b-256 |
580371e92bfdc1eddd08ed3aaf9a40dc48a713a416e960631b08c1e40cbc244f
|
Provenance
The following attestation bundles were made for assertpy2-2.9.1.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.9.1.tar.gz -
Subject digest:
8fb5b9b7dfa05b29f376918460385b1262548bd1087994d739193ef2b2907e95 - Sigstore transparency entry: 1953957152
- Sigstore integration time:
-
Permalink:
Solganis/assertpy2@05b07d8592aeecfd525845209330a6285bedf9d5 -
Branch / Tag:
refs/tags/v2.9.1 - Owner: https://github.com/Solganis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05b07d8592aeecfd525845209330a6285bedf9d5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file assertpy2-2.9.1-py3-none-any.whl.
File metadata
- Download URL: assertpy2-2.9.1-py3-none-any.whl
- Upload date:
- Size: 70.2 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 |
8f6482d0962fea2957e8fd92cfd17a6f344b7acd52f0fb626a426d122032ccff
|
|
| MD5 |
d25d36ecde37d65b1a08d97265eb2dad
|
|
| BLAKE2b-256 |
61317cc050f1bbb29ba3d4f72499786668397a243062f080b21c51d7e79c6d65
|
Provenance
The following attestation bundles were made for assertpy2-2.9.1-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.9.1-py3-none-any.whl -
Subject digest:
8f6482d0962fea2957e8fd92cfd17a6f344b7acd52f0fb626a426d122032ccff - Sigstore transparency entry: 1953957277
- Sigstore integration time:
-
Permalink:
Solganis/assertpy2@05b07d8592aeecfd525845209330a6285bedf9d5 -
Branch / Tag:
refs/tags/v2.9.1 - Owner: https://github.com/Solganis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05b07d8592aeecfd525845209330a6285bedf9d5 -
Trigger Event:
release
-
Statement type: