mit einem Klick
add-test
// Use when writing tests for EmbodiChain modules, including observation functors, reward functors, solvers, sensors, environments, or any Python module
// Use when writing tests for EmbodiChain modules, including observation functors, reward functors, solvers, sensors, environments, or any Python module
Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment
Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment
Use when creating a new task environment for EmbodiChain, including expert demonstration tasks, RL tasks or any EmbodiedEnv subclass
Write benchmark scripts for EmbodiChain modules following project conventions
Create a pull request for EmbodiChain following the project's PR template and conventions, including selecting proper GitHub repository labels
Use before committing or creating a PR for EmbodiChain to verify code style, headers, annotations, exports, and docstrings pass CI checks
| name | add-test |
| description | Use when writing tests for EmbodiChain modules, including observation functors, reward functors, solvers, sensors, environments, or any Python module |
Write tests following EmbodiChain's conventions and patterns.
Tests mirror the source tree under tests/:
embodichain/lab/sim/solvers/pytorch_solver.py → tests/sim/solvers/test_pytorch_solver.py
embodichain/lab/gym/envs/managers/rewards.py → tests/gym/envs/managers/test_reward_functors.py
embodichain/toolkits/graspkit/pg_grasp/foo.py → tests/toolkits/test_pg_grasp.py
embodichain/lab/gym/envs/tasks/rl/push_cube.py → tests/gym/envs/tasks/test_push_cube.py
Rules:
test_<module>.pyembodichain/ structure under tests/__init__.py files in new tests/ subdirectories if neededUse when: testing functors, utility functions, pure math, config validation — anything that doesn't need a SimulationManager.
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
# ----------------------------------------------------------------------------
from __future__ import annotations
import pytest
import torch
from embodichain.my_module import my_function
def test_expected_output():
result = my_function(input_value)
assert result == expected_value
def test_edge_case():
result = my_function(edge_input)
assert result is not None
Use when: tests need SimulationManager, GPU setup, or must run in a specific order. Share state via setup_method/teardown_method.
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
# ----------------------------------------------------------------------------
from __future__ import annotations
import pytest
import torch
from embodichain.lab.sim import SimulationManager, SimulationManagerCfg
class TestMySimComponent:
def setup_method(self):
config = SimulationManagerCfg(headless=True, sim_device="cpu")
self.sim = SimulationManager(config)
# ... setup ...
def teardown_method(self):
self.sim.destroy()
def test_basic_behavior(self):
result = self.sim.do_something()
assert result == expected_result
def test_raises_on_bad_input(self):
with pytest.raises(ValueError):
self.sim.do_something(bad_input)
Most functor tests don't need a live simulation. Use mock objects following the pattern in tests/gym/envs/managers/test_reward_functors.py:
from unittest.mock import MagicMock, Mock
class MockSim:
"""Mock simulation for functor tests."""
def __init__(self, num_envs: int = 4):
self.num_envs = num_envs
self.device = torch.device("cpu")
self._rigid_objects: dict = {}
def get_rigid_object(self, uid: str):
return self._rigid_objects.get(uid)
def add_rigid_object(self, obj):
self._rigid_objects[obj.uid] = obj
class MockEnv:
"""Mock environment for functor tests."""
def __init__(self, num_envs: int = 4):
self.num_envs = num_envs
self.device = torch.device("cpu")
self.sim = MockSim(num_envs)
Key points for mock objects:
num_envs and device attributes (functors use these)MagicMock(uid="...") for SceneEntityCfg parametersAsk the user:
Map the source path to test path:
embodichain/<subpath>/<module>.py → tests/<subpath>/test_<module>.py
Check if the test file already exists — append new test classes/functions if so.
digraph test_style {
rankdir=LR;
"Needs SimulationManager?" -> "Class style" [label="yes"];
"Needs SimulationManager?" -> "pytest style" [label="no"];
"Tests share state/order?" -> "Class style" [label="yes"];
"Tests share state/order?" -> "pytest style" [label="no"];
}
Use the appropriate template (pytest or class style above).
Rules:
from __future__ import annotations — after header, before importstest_<scenario> (descriptive, not just test_foo)if __name__ == "__main__" BlockInclude this for tests that support optional visual/interactive debugging:
if __name__ == "__main__":
# For visual debugging: set is_visual=True when calling env methods
test_obj = TestMyComponent()
test_obj.setup_method()
# ... manually run test logic ...
# Single file
pytest tests/<subpath>/test_<module>.py -v
# Single test function
pytest tests/<subpath>/test_<module>.py::test_expected_output -v
# Single test class method
pytest tests/<subpath>/test_<module>.py::TestMyClass::test_basic_behavior -v
blackblack tests/<subpath>/test_<module>.py
| Convention | Rule |
|---|---|
| File header | Apache 2.0 copyright block (same 15 lines as source) |
| File naming | test_<module>.py |
| Function naming | test_<scenario> |
from __future__ | Required after header |
| Magic numbers | Define as named constants with explanatory comments |
| Simulation tests | Initialize/teardown in setup_method/teardown_method |
| Pure-logic tests | Use mock objects, no real sim |
SceneEntityCfg | Use MagicMock(uid="...") in tests |
| Assertions | assert, pytest.approx, torch.allclose, pytest.raises |
| Entry block | if __name__ == "__main__" for visual debugging support |
| Mistake | Fix |
|---|---|
| Missing Apache header on test file | Copy the 15-line copyright block |
Using real SimulationManager for functor tests | Use MockEnv/MockSim — much faster, no GPU needed |
| Hardcoded numbers without explanation | Define as EXPECTED_DISTANCE = 0.5 # cube at origin, target at (0.5, 0, 0) |
| Testing multiple concepts in one function | Split into separate test_<scenario> functions |
Forgetting teardown_method | Always call self.sim.destroy() in teardown |
Not running black on test file | CI checks all files including tests |
| Action | Command |
|---|---|
| Run all tests | pytest tests/ |
| Run single file | pytest tests/<path>/test_<name>.py -v |
| Run single test | pytest tests/<path>::test_<name> -v |
| Run with print output | pytest -s tests/<path>/test_<name>.py |
| Format | black tests/<path>/test_<name>.py |