Skip to main content

Decorators

The four decorators in roboticks are the SDK’s smallest surface and the one users touch most. All four work on any callable that pytest collects — functions, methods, parametrized variants, async tests. They stack in any order.

@confirms(*req_ids)

Records which requirement IDs the decorated test confirms. The pytest plugin reads this and emits a <property name="roboticks.confirms" value="REQ-001,REQ-014"/> into the JUnit XML at session-end.

Signature

Behaviour

  • Stacking@confirms("A") on a class plus @confirms("B") on a method unions to {"A", "B"}. Useful when an entire test class addresses one umbrella requirement and individual methods address sub-requirements.
  • Parametrize — A @pytest.mark.parametrize variant inherits the function’s @confirms set; all branches confirm the same requirement IDs. If you need different requirements per branch, split into multiple test functions.
  • Skipped tests — A skipped test still records its @confirms (so the matrix shows which requirements are intended to be covered, even when the test didn’t run on this commit). The matrix surfaces them with a “skipped” badge.

Example

@tags(*tags)

Free-form labels for filtering. The platform doesn’t interpret them — they are pass-through metadata for your filters, dashboards, and policies.

Signature

Conventions

By community convention (not enforced):

Example

The platform exposes tag-based test filters in Project → Tests → Filter by tag.

@deadline(milliseconds=int)

Fails the test if its wall-clock duration exceeds the budget. Useful for soft real-time guarantees that don’t otherwise show up as an assertion.

Signature

Behaviour

  • The decorator wraps the test body in a time.monotonic() measurement and raises DeadlineExceeded (an AssertionError subclass) if the test runs over.
  • Reported in JUnit XML as <property name="roboticks.deadline_ms" value="100"/>.
  • The platform surfaces the actual duration vs the deadline in the run detail. A test that passes its assertions but blows the deadline is failed.

Example

Interaction with pytest timeouts

@deadline and pytest-timeout are independent. pytest-timeout kills the process; @deadline only fails the test. Use both for hard-stop budgets:

@requires_sim(engine, *, gpu=False)

Tells the job router the test needs a simulation runtime.

Signature

Behaviour

  • Reported in JUnit XML as <property name="roboticks.requires_sim" value="gazebo:gpu"/> or "gazebo" if gpu=False.
  • The platform’s scheduler refuses to dispatch the test to a runner that doesn’t satisfy the engine + GPU label.
  • Local pytest runs do not skip the test — they run it. If sim isn’t available locally the test will fail at the rclpy step, which is the right behaviour: a developer needs to know they’re trying to run a sim test without a sim.

Example

Stacking order

Decorators are evaluated bottom-up (closest-to-function first), but for these four the order doesn’t change behaviour — they are independent metadata. Pick a convention and stick with it. The convention in our examples:

What about @pytest.mark.X?

Roboticks decorators are not pytest markers. They mutate the function’s attributes; the pytest plugin reads those attributes at collection time. This avoids pytest’s marker-registration warnings and keeps @confirms working on classes (where @pytest.mark.X semantics are subtle). You can still use pytest markers freely. They land in the JUnit XML as standard pytest properties; they don’t conflict with the roboticks.* namespace.

Next

Pytest plugin

How the plugin reads these decorators and writes JUnit properties.

Assertions

The rclpy-aware companion APIs.

Sim runners

What @requires_sim triggers on the runner side.

Wire contract

The JUnit-with-confirms schema these decorators feed into.