|
| 1 | +# Stability |
| 2 | + |
| 3 | +What you can build on, and what enforces it. Every row below is held by a test that fails CI, not by |
| 4 | +an intention. |
| 5 | + |
| 6 | +## The contract |
| 7 | + |
| 8 | +| You can depend on | Enforced by | |
| 9 | +|---|---| |
| 10 | +| The 36 names `assertpy2` exports, and the fields of every record it hands you | [`test_public_surface.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_public_surface.py) pins both against a hand-written list | |
| 11 | +| Every assertion the type checker offers you existing at runtime | [`test_protocol_parity.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_protocol_parity.py) walks all ten protocols | |
| 12 | +| The type your chain has after each step | [`test_typing.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_typing.py), 123 `assert_type` checks under ty, mypy `--strict` and Pyright, zero suppressions | |
| 13 | +| One relation keeping one name across the API | [`test_api_vocabulary.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_api_vocabulary.py) | |
| 14 | +| The three-method `Matcher` protocol your custom matchers implement | [`test_matcher_parity.py`](https://github.com/Solganis/assertpy2/blob/main/tests/test_matcher_parity.py) | |
| 15 | +| The Allure attachment schema | versioned in its own `format` field, so a consumer branches on a number rather than guessing | |
| 16 | +| Your assertions keeping their verdict | 4100 tests at 100 % branch coverage, plus mutation testing | |
| 17 | + |
| 18 | +The records in the first row are `AssertionOutcome`, `MatchResult`, `DiffEntry`, `DiffResult`, `Step`, |
| 19 | +`PollSample` and `PollTrace`. Fields are added, never renamed or removed, inside a major version. |
| 20 | + |
| 21 | +Documentation is held to the same bar: every example in these guides is executed and type-checked in |
| 22 | +CI, so a snippet you copy is one that ran. |
| 23 | + |
| 24 | +## Reading a failure from code |
| 25 | + |
| 26 | +Failures carry [structured data](../guides/errors.md#structured-errors) for exactly this. Reach for it |
| 27 | +rather than for the rendered text: |
| 28 | + |
| 29 | +| You want | Use | |
| 30 | +|---|---| |
| 31 | +| the values that were compared | `failure.actual`, `failure.expected` | |
| 32 | +| where they differ | `failure.diff.entries`, each with a `path` | |
| 33 | +| a location you can walk in code | [`entry.steps`](../guides/errors.md#paths-a-program-can-follow) | |
| 34 | +| every failure a soft block collected | `failure.failures` | |
| 35 | +| a verdict without an exception | [`check()`](../guides/errors.md#asking-instead-of-asserting) | |
| 36 | + |
| 37 | +Those are covered by the table above. **Message wording is not**: it improves in minor releases, so a |
| 38 | +`pytest.raises(match=...)` written against our phrasing, or a snapshot of a failing run, is the one |
| 39 | +thing that predictably needs updating. Match on the exception type and read the fields. |
| 40 | + |
| 41 | +## Upgrading |
| 42 | + |
| 43 | +Semantic versioning, read strictly: a new assertion is a minor, a patch carries fixes only. |
| 44 | + |
| 45 | +Two kinds of change ship in a minor, and the release notes list each one under **Behaviour changes**: |
| 46 | + |
| 47 | +- **an input that was silently wrong starts raising**, such as an empty prefix that no value could fail |
| 48 | +- **a wrong verdict is corrected**, so an assertion that passed and should not have begins to fail |
| 49 | + |
| 50 | +Both are the reason to read that section before upgrading. Nothing else in a minor is designed to |
| 51 | +change what your suite reports. |
| 52 | + |
| 53 | +## Not part of the API |
| 54 | + |
| 55 | +Anything whose name starts with `_`, and the module an assertion happens to live in. The reference |
| 56 | +lists `matches_structure()` because you can call it from `assert_that()`, and its anchor reads |
| 57 | +`assertpy2._satisfies.SatisfiesMixin.matches_structure` because that is where the code sits. |
| 58 | +Importing from there is not supported. |
| 59 | + |
| 60 | +Python 3.10 and up. Dropping a version that upstream still supports would be a major. |
| 61 | + |
| 62 | +Code written against [assertpy](../getting-started/migration.md) keeps working, and that holds for |
| 63 | +refactors: an internal change that would break a documented assertpy call is not internal. |
0 commit comments