Migration from assertpy¶
assertpy2 is a drop-in, fully typed replacement for the original
assertpy - the same assert_that(...) API, so for most projects
the switch is a single import.
TL;DR
On Python 3.10+, replace from assertpy import ... with from assertpy2 import ... and run your
tests. The assertions you already use carry over unchanged.
Before you start¶
- Python 3.10+ is required. The original assertpy ran on Python 2.7 and 3.x. assertpy2 targets 3.10 through 3.15. If you are on an older interpreter, upgrade Python first. That is the only hard requirement of the migration.
- No runtime dependencies on Python 3.11+. On 3.10 a single tiny backport (
typing_extensions) is pulled in automatically. The extras ([json],[data],[allure],[behave]) stay opt-in.
Switch the import¶
Swap the dependency first. Uninstall assertpy, install assertpy2, update your
requirements/pyproject, then switch the imports:
If you import the module instead of its names, alias it so existing call sites keep working:
A project-wide find-and-replace of from assertpy import to from assertpy2 import is usually the
entire migration.
Automate it across a project
grep -rl --include="*.py" "assertpy" . \
| xargs sed -i 's/from assertpy import/from assertpy2 import/g'
from assertpy import lines. For
import assertpy usages, prefer the alias shown above.
What stays the same¶
assertpy2 is a superset of the original, so the assertions you already use are present and produce the same results:
- the
assert_that()entry point and fluent (return self) chaining - strings, numbers, lists, tuples, dicts, sets, booleans,
None, dates, files, and objects - dynamic
has_<name>()assertions andextracting()withfilterandsort - soft assertions, expected exceptions (
raises().when_called_with()),fail(),assert_warn(), snapshot testing, andadd_extension() - the existing failure messages, apart from the extra detail noted below.
Backward-compatible failures
Failing assertions now raise AssertionFailure, a subclass of AssertionError. Existing
except AssertionError handlers keep working unchanged. The subclass simply carries extra
structured data.
If your tests assert on message text
assertpy2 writes more into some failures than the original did, usually by adding to the end of
the message. A pytest.raises(match=...) or a substring check keeps passing.
What a substring check does not survive is a change of order: a mapping's keys are printed in
the order they were written rather than sorted, so a check pinned to the sorted rendering needs
updating. Comparing a whole message with == breaks on both counts, on the switch and between
assertpy2 versions.
Prefer matching a substring. Better still, skip the text and assert on the structured data the
failure carries: AssertionFailure.actual, .expected and .diff.
What improves automatically¶
You get these the moment you switch, without touching any test code:
- Thread-safe and async-safe soft assertions. State is isolated per thread and per
asyncio.Taskviacontextvars, so soft assertions are safe underpytest-xdistandasyncio. The original's soft assertions were not thread-safe. - Structured failures and rich diffs. Failures carry
.actual/.expected/.diff, and the diff is rendered into the message itself, so it shows inunittest, plain scripts and CI logs. Under pytest the auto-registered plugin renders it as a colored report section instead, recursive for lists, dicts, dataclasses, namedtuples, attrs classes and Pydantic models. Setassertpy2_diff = "off"to turn that section off. - Static typing. With
py.typedand@overloadprotocols your editor filters autocomplete by the value's type, and a type checker flags misuse before the tests run. See Type Safety.
What you can now adopt¶
New capabilities the original never had, ready whenever you want them:
- Composable matchers, reusable across assertions and the plain
==form. - Structural matching for API-response shapes.
- Typed narrowing (
.value) and contract testing for typed API responses. - Exception cause chains and groups.
- The collection pipeline and universal negation.
- Async and blocking polling for eventual consistency.
- JSON Path / Schema, regex group extraction, and bytes assertions.
See the comparison for how the two read side by side, and stability for what is covered by a compatibility promise and what is not.