Test
The NeMo Lens test suite is small and focused. Over 180 tests cover every public API, with a strong emphasis on:
- State isolation ensures that tests touching global OTel state reset it before and after running.
- No-op equivalence ensures that the behavior of
fallbacks.pymatches the real API. - Configuration edge cases cover every environment variable combination and validation path.
Run Tests
Layout
Global State Isolation
The OTel SDK stores providers globally (trace._TRACER_PROVIDER, metrics._METER_PROVIDER). NeMo Lens stores enabled span groups globally. Tests that touch these must reset between runs; otherwise, the second test inherits the state of the first test.
conftest.py has three autouse fixtures:
Because reset_span_groups clears the enabled set before every test, a test that needs a group active must opt in explicitly. The set_enabled_span_groups(...) function, which is a top-level export from nemo.lens, provides the escape hatch for enabling groups inside a test.
A third autouse fixture, reset_strategy_registry, snapshots nemo.lens.strategies._REGISTRY (under _REGISTRY_LOCK) before each test and restores it afterward, so custom export strategies registered through register_export_strategy do not leak between tests:
_reset_otel_globals() resets five pieces of state:
The Once() pointers are the OTel SDK’s internal “was this set?” flag. Without resetting them, setup_telemetry in a test would install a new provider, but the SDK would log “provider already set” and silently use the previous one.
Capture Spans
Tests that assert on span content use InMemorySpanExporter (shipped in conftest.py):
For metrics, use InMemoryMetricReader from the OTel SDK.
Test Fallbacks
tests/test_fallbacks.py asserts that nemo.lens.fallbacks signatures match the real API and behave as no-ops. Whenever you add a parameter to managed_span or trace_fn, also add it to fallbacks.py and extend the test.
Test Double-Init
Tests that legitimately need to call setup_telemetry multiple times in one test (e.g., simulating multiple ranks) pass _allow_reinit=True:
Test Distributed Helpers
broadcast_trace_context uses torch.distributed, which cannot run in a single-process test without mocks. The distributed tests use torch.distributed.init_process_group(backend='gloo', ...) with a single rank; therefore, the broadcast becomes a no-op, but the code path exercises correctly.
For genuinely multi-rank behavior, tests would need to spawn subprocesses; currently, the single-rank path and manual carrier construction cover the contract.
Test the OTel Interface of RankAwareSampler
The sampler is wrapped in a try/except block inside should_sample to fall back to bool if the SDK is not installed, which is covered by a separate test.
Lint Code
Pre-commit runs both (the ruff and ruff-format hooks in .pre-commit-config.yaml). CI runs pre-commit run --all-files and rejects PRs that fail it.
What Is Not Tested
- Actual export to a collector. That is the SDK’s job; mocking it correctly requires more effort than it is worth.
- Long-running performance. The
tests/directory exercises correctness, not throughput. - Integration with consumer libraries. These libraries have their own test suites (
Megatron-LM/tests/unit_tests/telemetry/, etc.).
When adding features that interact with a consumer, add a corresponding test in the consumer repository. NeMo Lens tests must remain self-contained.