testing
Helpers for writing pytest tests over SysMLv2/KerML models.
This module provides small, dependency-light utilities for asserting
properties about models loaded with syside:
describe()renders a human-readable identifier for a model element, used in assertion failure messages.assert_all(),assert_any()andassert_none()assert quantified predicates over an iterable of elements and raiseAssertionErrorwith a clear, multi-line message that names the offending elements.model_fixture()builds a pytest fixture that loads a model once per scope (session by default), so a test suite does not reload it per test.
The assertion helpers consume their input iterable exactly once and call the
describe callback only for the (capped) set of elements that actually
appear in a failure message, so they stay cheap on large models and on the
success path.
Index
Functions ¶
Return a default human-readable identifier for a model element. |
||
Assert that |
||
Assert that |
||
Assert that |
||
Assert that |
||
Assert that |
||
Assert that the directed graph over |
||
Assert that every source can reach at least one target. |
||
Build a pytest fixture that loads a model once per scope. |
Functions
- describe(item: object) str[source]
Return a default human-readable identifier for a model element.
If
itemhas a non-Nonequalified_name, returnf"{type(item).__name__} {item.qualified_name}"(the qualified name renders to e.g.Pkg::Sub::Name). Otherwise, or if attribute access raises, fall back torepr(item).This function never raises.
- assert_all(items: Iterable[syside.testing.assert_all.T], predicate: Callable[[syside.testing.assert_all.T], object], *, describe: Callable[[syside.testing.assert_all.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that
predicateis truthy for every item initems.Passes vacuously when
itemsis empty. On failure, raisesAssertionErrornaming the items for whichpredicatewas falsy (the counterexamples), listing at mostlimitof them.predicateis called exactly once per item.describeis called only on the items that appear in the failure message.
- assert_any(items: Iterable[syside.testing.assert_any.T], predicate: Callable[[syside.testing.assert_any.T], object], *, describe: Callable[[syside.testing.assert_any.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that
predicateis truthy for at least one item initems.Fails when
itemsis empty. On failure, raisesAssertionErrorstating that none of the examined items satisfied the predicate, listing at mostlimitof the examined items.predicateis called exactly once per item, and short-circuits as soon as a truthy result is found.describeis called only on the items that appear in the failure message.
- assert_none(items: Iterable[syside.testing.assert_none.T], predicate: Callable[[syside.testing.assert_none.T], object], *, describe: Callable[[syside.testing.assert_none.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that
predicateis truthy for no item initems.Passes vacuously when
itemsis empty. On failure, raisesAssertionErrornaming the items for whichpredicatewas truthy, listing at mostlimitof them.predicateis called exactly once per item.describeis called only on the items that appear in the failure message.
- assert_implies(items: Iterable[syside.testing.assert_implies.T], when: Callable[[syside.testing.assert_implies.T], object], then: Callable[[syside.testing.assert_implies.T], object], *, describe: Callable[[syside.testing.assert_implies.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that
when(item)truthy impliesthen(item)truthy.For every item, if
whenis truthy thenthenmust be truthy too. Passes vacuously whenitemsis empty (or no item satisfieswhen). On failure, raisesAssertionErrornaming the items for which the antecedent held but the consequent failed (the counterexamples), listing at mostlimitof them.whenis called exactly once per item;thenis called only for items wherewhenis truthy (short-circuited otherwise).describeis called only on the items that appear in the failure message.
- assert_foreach_unique(items: Iterable[syside.testing.assert_foreach_unique.T], target: Callable[[syside.testing.assert_foreach_unique.T], Hashable], *, describe: Callable[[syside.testing.assert_foreach_unique.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that
targetyields a distinct value for each item.That is,
targetis injective overitems. Passes whenitemsis empty or all target values are distinct. On failure, raisesAssertionErrornaming the target values shared by more than one item, listing at mostlimitcolliding values; for each, the items that share it are listed (also capped atlimit, with a…suffix).targetis called exactly once per item.describeis called only on the items that appear in the reported collisions.
- assert_acyclic(nodes: Iterable[syside.testing.assert_acyclic.T], successors: Callable[[syside.testing.assert_acyclic.T], Iterable[syside.testing.assert_acyclic.T]], *, describe: Callable[[syside.testing.assert_acyclic.T], str] = describe, label: str | None = None) None[source]
Assert that the directed graph over
nodeshas no directed cycle.The graph has
nodesas its (initial) vertices; any node returned bysuccessorsis also part of the graph even if it is not innodes. A node listed among its ownsuccessorsis a length-1 cycle.Traversal is an iterative depth-first search with an explicit stack (never recursion), so graphs thousands of nodes deep do not hit the Python recursion limit. Passes when
nodesis empty or the graph is acyclic. On failure, raisesAssertionErrornaming one concrete cycle as a path.describeis called only on the nodes of the reported cycle.
- assert_reachable(sources: Iterable[syside.testing.assert_reachable.T], targets: Iterable[syside.testing.assert_reachable.T], successors: Callable[[syside.testing.assert_reachable.T], Iterable[syside.testing.assert_reachable.T]], *, describe: Callable[[syside.testing.assert_reachable.T], str] = describe, label: str | None = None, limit: int = 10) None[source]
Assert that every source can reach at least one target.
Reachability follows
successorsforward. A source that is itself a target reaches trivially (distance 0). Passes whensourcesis empty. Whensourcesis non-empty buttargetsis empty, every source fails.On failure, raises
AssertionErrornaming the sources that cannot reach any target (the counterexamples), listing at mostlimitof them.describeis called only on the unreachable sources that appear in the failure message.
- model_fixture(*paths: str | os.PathLike[str], sysml_source: str | None = None, kerml_source: str | None = None, scope: syside.testing.Scope = 'session', warnings_as_errors: bool = False) Callable[[], syside.Model][source]
Build a pytest fixture that loads a model once per scope.
Provide the model through exactly one of
syside.load_model()’s entrypoints – there is no guessing between a path and inline text:positional
paths– one or more file or directory paths, orsysml_source– a single inline SysML v2 source string, orkerml_source– a single inline KerML source string.
Passing none, or more than one, of these raises
ValueErrorimmediately (when the fixture is defined, not when a test runs).The fixture loads the model once per
scope("session"by default, so the suite does not reload it per test) and returns the loadedsyside.Model. If loading fails to compile – either becausesyside.load_model()raisessyside.ModelError, or because it returns diagnostics for whichdiagnostics.contains_errors()is true – the fixture fails with anAssertionErrordescribing the diagnostics. Withwarnings_as_errors=True, diagnostics containing any warnings fail the fixture the same way.Intended use in a
conftest.py:import syside.testing model = syside.testing.model_fixture("path/to/models")
or with inline source for a focused test:
model = syside.testing.model_fixture(sysml_source="package P { part def A; }")
The returned object is a pytest fixture; reference it by the name you bind it to (
modelabove) as a test argument.