testing
The cross-cutting testing umbrella for robium. Robotics apps fail in layers a
generic test suite misses — a node that never receives a message, a launch
file that starts everything except the one node that mattered, a policy that
looks fine on paper but never reaches the goal in sim. This skill frames the
robotics-specific test pyramid and states the non-negotiable bar: a robium
build is not done until its smoke test passes. It does not re-teach
launch_testing/pytest mechanics for ROS 2 (that's ros2) or the
lerobot-eval CLI (that's lerobot) — it frames both as test-pyramid layers
and routes to them.
When to use this skill
- Setting up tests for any new robium project — this should be planned into
the build from the start, not bolted on after the fact.
- The trigger phrases in the description: 'test the robot app', 'how do I test
this node', 'smoke test', 'regression test in sim'.
- Before claiming any robotics app or sample works — a claim of "done" without
a passing smoke test is not verified, it's a guess.
- Load alongside whatever skill is building the thing under test —
ros2/
nav2/gazebo for the navigation vertical, lerobot for the manipulation
vertical — this skill supplies the test framing, not a replacement for
either.
- Cross-references — go to the sibling skill instead when the question is:
- ROS 2
launch_testing/pytest mechanics themselves (fixtures, process
actions, assertions) → the upstream launch_testing README and ROS 2
testing docs (linked in References). The ros2 skill does not yet
carry launch_testing content — until it does, go upstream directly
rather than hunting for it there. This skill frames launch/node testing
as a pyramid layer either way.
- The
lerobot-eval CLI and its flags → lerobot. This skill frames policy
eval as a test-pyramid layer with a pass/fail bar; lerobot owns the eval
mechanics.
- Setting up the simulator a regression test runs against →
gazebo or
isaac-sim (or simulation if the choice isn't made yet).
- General (non-robotics) testing practices — unrelated to this skill; use
whatever generic testing guidance already applies to the language/
framework in question.
- The whole-stack decision this feeds into →
architect (plans testing into
the brief, doesn't bolt it on later).
Key directives
- Delegation posture: route + embed the pyramid framing. The test-pyramid
structure, what belongs at each layer, and the trial-run bar live here;
launch_testing/pytest syntax and lerobot-eval flags live downstream.
Never re-teach either mechanism's syntax in this skill.
- A sample app is not done until its smoke test passes. This is the
concrete acceptance bar for any robium trial run: "it builds" or "it starts
without crashing" is not sufficient — a launch/node-level smoke test
actually passing is the minimum evidence of "done." Treat a missing or
failing smoke test as an open task, not a detail to fix later.
- Test at the right layer — don't push everything into sim. Logic that
doesn't depend on ROS or a running robot belongs in a plain unit test, not a
slow sim scenario; reserve sim-based regression tests for behavior that
genuinely requires the simulated environment. See the pyramid in Decision
guidance.
- Determinism is what makes a test a test. A sim scenario or policy eval
that isn't seeded/repeatable produces flaky results that get blamed on the
code instead of the test — apply
simulation's determinism guidance (seeded
physics, use_sim_time) and lerobot's small-scale-before-long-run
discipline (a short, deterministic eval run as a CI gate, not a long
training-scale rollout) to every automated test.
- Both verticals get equal treatment. The ROS/navigation vertical tests
with
launch_testing + pytest; the manipulation vertical tests with
deterministic small-scale policy eval runs. Neither is "the real test
framework" and the other an afterthought — plan both when a project spans
both verticals.
Quick start
1. Identify what layer a new piece of behavior belongs at using the
pyramid in Decision guidance — don't default to "write a sim test" for
everything.
2. For ROS 2 apps: write plain pytest for ROS-independent logic, and a
launch_testing case for anything that needs a running node or launch file —
mechanics in the upstream launch_testing docs (see References). Run both
through colcon test as part of the build.
3. For ML/policy apps: treat a short, deterministic lerobot-eval run
against a fixed dataset/seed as the smoke test — mechanics in lerobot. A
handful of episodes with a stated success-rate threshold is enough to gate CI;
save large-scale eval runs for manual validation, not every CI run.
4. Before declaring a sample app or feature done, run its smoke test and
confirm it passes — this is the trial-run bar, not optional polish.
5. Wire the passing tests into CI so the bar holds on every change, not
just the first time.
Decision guidance
The test pyramid for robotics (narrower and faster at the top, broader and
slower at the bottom — most changes should be caught by a unit test, not a
full sim run):
| Layer |
What it checks |
Tool / pattern |
Owning skill |
| Unit |
Pure logic with no ROS/robot/sim dependency (a planner's math, a state-machine transition, a data-formatting function) |
pytest |
(generic — no robium skill needed) |
| Node / launch smoke |
A node starts, a launch file brings up the expected set of nodes without crashing, expected topics/services appear |
launch_testing + pytest |
upstream launch_testing docs (see References; not yet in ros2) |
| Sim scenario / regression |
End-to-end behavior in a scripted scenario (robot reaches a goal, avoids an obstacle, completes a manipulation task) run headless and deterministically |
A seeded Gazebo/Isaac Sim run driven by a test script, checked against expected outcomes |
gazebo, isaac-sim, simulation |
| Policy eval |
A learned policy's success rate over a small, fixed, seeded set of episodes meets a stated threshold |
Deterministic small-scale lerobot-eval run as a pass/fail gate |
lerobot |
The trial-run bar: for any sample app or feature this pyramid covers, the
minimum passing bar before calling it done is the node/launch smoke layer (ROS
vertical) or a policy-eval smoke run (manipulation vertical) — sim-scenario
and full-scale eval are the next layer up, expected for anything beyond a
first working sample.
Platform gotchas
- CI runners are headless by default — sim-scenario tests must run
headless (Gazebo server-only mode, Isaac Sim's
runheadless.sh) rather than
assuming a display; route the general headless strategy to environments if
it isn't already decided.
- GPU-gated eval inherits its gate in CI too. A policy-eval smoke test
that needs Isaac Sim inherits that skill's NVIDIA RTX GPU floor — confirm
the CI runner actually has a qualifying GPU before wiring an Isaac-Sim-backed
eval into every pipeline run, or keep CI on a
gazebo/CPU-only eval path and
reserve GPU-backed runs for manual/scheduled checks.
- macOS CI runners can't run the ROS 2 layers natively — the
node/launch-smoke and sim-scenario layers need Docker on macOS, same as
local development (see
ros2's and gazebo's macOS gotchas); plan CI
images accordingly rather than assuming a native macOS runner works.
Customization
- Small demo vs. a fleet-scale project: a one-robot sim demo may only need
the top two pyramid layers (unit + smoke) to meet the trial-run bar; a
production-bound project should build out sim-scenario and eval-threshold
layers too — scale the pyramid's depth to the project's stakes, but never
skip the smoke layer entirely.
- Adding a new vertical or module: extend the same four-layer pyramid
rather than inventing a project-specific test taxonomy — the layer names and
owning skills stay consistent across robium projects.
References
Changelog
- 1.0.1 (2026-07-11): nav-trial absorption — fixed the launch_testing
routing dead-end: three spots routed "mechanics → ros2" but the ros2
skill has no launch_testing content; routes now point at the upstream
docs until ros2 grows that section. Trial-run bar + smoke shape confirmed
✓ under real load (one-command compose smoke, exit-code chain).
1---2name: testing-33description: Test-driven robotics development: smoke tests for launch files, sim-based regression tests, node-level unit tests, policy eval as a test, and CI patterns for robotics repos. Use when: 'test the robot app', 'how do I test this node', 'smoke test', 'regression test in sim', setting up tests for a new robotics project, or before claiming any robotics app works. Applies to both verticals: launch_testing and pytest for ROS 2 apps; deterministic small-scale eval runs for ML policies. Load alongside whatever skill is building the thing under test. Not for: general (non-robotics) testing practices.4---56# testing78The cross-cutting testing umbrella for robium. Robotics apps fail in layers a9generic test suite misses — a node that never receives a message, a launch10file that starts everything except the one node that mattered, a policy that11looks fine on paper but never reaches the goal in sim. This skill frames the12robotics-specific test pyramid and states the non-negotiable bar: a robium13build is not done until its smoke test passes. It does not re-teach14`launch_testing`/pytest mechanics for ROS 2 (that's `ros2`) or the15`lerobot-eval` CLI (that's `lerobot`) — it frames both as test-pyramid layers16and routes to them.1718## When to use this skill1920- Setting up tests for any new robium project — this should be planned into21 the build from the start, not bolted on after the fact.22- The trigger phrases in the description: 'test the robot app', 'how do I test23 this node', 'smoke test', 'regression test in sim'.24- Before claiming any robotics app or sample works — a claim of "done" without25 a passing smoke test is not verified, it's a guess.26- Load alongside whatever skill is building the thing under test — `ros2`/27 `nav2`/`gazebo` for the navigation vertical, `lerobot` for the manipulation28 vertical — this skill supplies the test framing, not a replacement for29 either.30- Cross-references — go to the sibling skill instead when the question is:31 - ROS 2 `launch_testing`/pytest mechanics themselves (fixtures, process32 actions, assertions) → the upstream launch_testing README and ROS 233 testing docs (linked in References). The `ros2` skill does not yet34 carry launch_testing content — until it does, go upstream directly35 rather than hunting for it there. This skill frames launch/node testing36 as a pyramid layer either way.37 - The `lerobot-eval` CLI and its flags → `lerobot`. This skill frames policy38 eval as a test-pyramid layer with a pass/fail bar; `lerobot` owns the eval39 mechanics.40 - Setting up the simulator a regression test runs against → `gazebo` or41 `isaac-sim` (or `simulation` if the choice isn't made yet).42 - General (non-robotics) testing practices — unrelated to this skill; use43 whatever generic testing guidance already applies to the language/44 framework in question.45 - The whole-stack decision this feeds into → `architect` (plans testing into46 the brief, doesn't bolt it on later).4748## Key directives4950- **Delegation posture: route + embed the pyramid framing.** The test-pyramid51 structure, what belongs at each layer, and the trial-run bar live here;52 `launch_testing`/pytest syntax and `lerobot-eval` flags live downstream.53 Never re-teach either mechanism's syntax in this skill.54- **A sample app is not done until its smoke test passes.** This is the55 concrete acceptance bar for any robium trial run: "it builds" or "it starts56 without crashing" is not sufficient — a launch/node-level smoke test57 actually passing is the minimum evidence of "done." Treat a missing or58 failing smoke test as an open task, not a detail to fix later.59- **Test at the right layer — don't push everything into sim.** Logic that60 doesn't depend on ROS or a running robot belongs in a plain unit test, not a61 slow sim scenario; reserve sim-based regression tests for behavior that62 genuinely requires the simulated environment. See the pyramid in Decision63 guidance.64- **Determinism is what makes a test a test.** A sim scenario or policy eval65 that isn't seeded/repeatable produces flaky results that get blamed on the66 code instead of the test — apply `simulation`'s determinism guidance (seeded67 physics, `use_sim_time`) and `lerobot`'s small-scale-before-long-run68 discipline (a short, deterministic eval run as a CI gate, not a long69 training-scale rollout) to every automated test.70- **Both verticals get equal treatment.** The ROS/navigation vertical tests71 with `launch_testing` + pytest; the manipulation vertical tests with72 deterministic small-scale policy eval runs. Neither is "the real test73 framework" and the other an afterthought — plan both when a project spans74 both verticals.7576## Quick start7778**1. Identify what layer a new piece of behavior belongs at** using the79pyramid in Decision guidance — don't default to "write a sim test" for80everything.8182**2. For ROS 2 apps:** write plain pytest for ROS-independent logic, and a83`launch_testing` case for anything that needs a running node or launch file —84mechanics in the upstream launch_testing docs (see References). Run both85through `colcon test` as part of the build.8687**3. For ML/policy apps:** treat a short, deterministic `lerobot-eval` run88against a fixed dataset/seed as the smoke test — mechanics in `lerobot`. A89handful of episodes with a stated success-rate threshold is enough to gate CI;90save large-scale eval runs for manual validation, not every CI run.9192**4. Before declaring a sample app or feature done, run its smoke test and93confirm it passes** — this is the trial-run bar, not optional polish.9495**5. Wire the passing tests into CI** so the bar holds on every change, not96just the first time.9798## Decision guidance99100**The test pyramid for robotics** (narrower and faster at the top, broader and101slower at the bottom — most changes should be caught by a unit test, not a102full sim run):103104| Layer | What it checks | Tool / pattern | Owning skill |105|---|---|---|---|106| Unit | Pure logic with no ROS/robot/sim dependency (a planner's math, a state-machine transition, a data-formatting function) | pytest | (generic — no robium skill needed) |107| Node / launch smoke | A node starts, a launch file brings up the expected set of nodes without crashing, expected topics/services appear | `launch_testing` + pytest | upstream launch_testing docs (see References; not yet in `ros2`) |108| Sim scenario / regression | End-to-end behavior in a scripted scenario (robot reaches a goal, avoids an obstacle, completes a manipulation task) run headless and deterministically | A seeded Gazebo/Isaac Sim run driven by a test script, checked against expected outcomes | `gazebo`, `isaac-sim`, `simulation` |109| Policy eval | A learned policy's success rate over a small, fixed, seeded set of episodes meets a stated threshold | Deterministic small-scale `lerobot-eval` run as a pass/fail gate | `lerobot` |110111**The trial-run bar:** for any sample app or feature this pyramid covers, the112minimum passing bar before calling it done is the node/launch smoke layer (ROS113vertical) or a policy-eval smoke run (manipulation vertical) — sim-scenario114and full-scale eval are the next layer up, expected for anything beyond a115first working sample.116117## Platform gotchas118119- **CI runners are headless by default** — sim-scenario tests must run120 headless (Gazebo server-only mode, Isaac Sim's `runheadless.sh`) rather than121 assuming a display; route the general headless strategy to `environments` if122 it isn't already decided.123- **GPU-gated eval inherits its gate in CI too.** A policy-eval smoke test124 that needs Isaac Sim inherits that skill's NVIDIA RTX GPU floor — confirm125 the CI runner actually has a qualifying GPU before wiring an Isaac-Sim-backed126 eval into every pipeline run, or keep CI on a `gazebo`/CPU-only eval path and127 reserve GPU-backed runs for manual/scheduled checks.128- **macOS CI runners can't run the ROS 2 layers natively** — the129 node/launch-smoke and sim-scenario layers need Docker on macOS, same as130 local development (see `ros2`'s and `gazebo`'s macOS gotchas); plan CI131 images accordingly rather than assuming a native macOS runner works.132133## Customization134135- **Small demo vs. a fleet-scale project:** a one-robot sim demo may only need136 the top two pyramid layers (unit + smoke) to meet the trial-run bar; a137 production-bound project should build out sim-scenario and eval-threshold138 layers too — scale the pyramid's depth to the project's stakes, but never139 skip the smoke layer entirely.140- **Adding a new vertical or module:** extend the same four-layer pyramid141 rather than inventing a project-specific test taxonomy — the layer names and142 owning skills stay consistent across robium projects.143144## References145146- Upstream: [ROS 2 testing overview](https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Testing/Testing-Main.html),147 [ROS 2 launch package (launch_testing source)](https://github.com/ros2/launch/blob/rolling/launch_testing/README.md),148 [colcon test-result / test workflow](https://colcon.readthedocs.io/en/released/reference/verb/test.html),149 [pytest documentation](https://docs.pytest.org/en/stable/).150- Sibling skills: `ros2` (`launch_testing`/pytest mechanics), `lerobot`151 (`lerobot-eval` mechanics and small-scale-run discipline), `gazebo` and152 `isaac-sim` (sim-scenario execution), `simulation` (determinism guidance153 this skill's sim-scenario layer relies on), `environments` (headless CI154 setup), `architect` (plans testing into the brief).155156## Changelog157158<!-- One dated line per battle-tested change, added by skill-author hardening sessions. -->159160- 1.0.1 (2026-07-11): nav-trial absorption — fixed the launch_testing161 routing dead-end: three spots routed "mechanics → ros2" but the ros2162 skill has no launch_testing content; routes now point at the upstream163 docs until ros2 grows that section. Trial-run bar + smoke shape confirmed164 ✓ under real load (one-command compose smoke, exit-code chain).