Skip to content

Fixtures & Utilities

Built-in pytest fixtures provided by the Zelos SDK plugins.

Core Fixtures

check - Assertion Framework

def test_example(check):
    """The check fixture is automatically provided."""
    check.that(value, "==", expected)
    check.that(signal, ">", threshold, within_duration_s=1.0)

Scope: function Auto-use: No (request explicitly)

zelos_session - SDK Initialization

# Automatically initializes SDK for all tests
# No need to explicitly use—it's auto-enabled

# Configure via command line:
pytest --zelos-trace --zelos-trace-url=grpc://localhost:2300 --zelos-log-level=debug

Scope: session Auto-use: Yes

Trace Fixtures

Recording Fixtures

These fixtures automatically manage trace file recording at different scopes (active only when --zelos-trace-file is set and scope matches --zelos-trace-file-scope). They require --zelos-local-artifacts-dir to be configured.

# These are auto-use based on --zelos-trace-file-scope setting

# trace_file_session - Records entire session
# trace_file_module - Records per module
# trace_file_class - Records per class
# trace_file_function - Records per function (default)

# Control via command line:
pytest --zelos-trace-file --zelos-local-artifacts-dir=./artifacts --zelos-trace-file-scope=class

All are auto-use when --zelos-trace-file is enabled.

File naming:

  • Default filename: {artifact_basename}-trace-{sanitized_nodeid}.trz
  • Override via pytest_zelos_trace_file_name(request) in conftest.py

trace_logging - Python Log Capture

def test_with_logging(trace_logging):
    """Capture Python logging to trace."""
    import logging

    logging.info("Test started")
    logging.error("Something failed")

Scope: session Auto-use: Yes (when --zelos-trace-logging enabled)

trace_stdout - Console Output

# Prints trace events to stdout
# Enable with: pytest --zelos-trace-stdout

def test_debug_output():
    """Events print to console for debugging."""
    source.log("event", {"value": 123})
    # Prints: [TRACE] source/event: value=123

Scope: session Auto-use: Yes (when --zelos-trace-stdout enabled)

Utilities

Check Configuration

from zelos_sdk.pytest.checker import check_config

@check_config(fail_fast=False)
def test_multiple_assertions(check):
    """Continue even if checks fail."""
    check.that(1, "==", 1)
    check.that(2, "==", 3)  # Fails but continues
    check.that(3, "==", 3)  # Still runs

Range / tolerance with the typed vocabulary

The typed Check API ships 21 built-in ops that flow over the wire to the agent. Most range and tolerance assertions are best expressed with these directly:

def test_voltage_in_range(check):
    """Express a range as two bounded checks; both post to the board."""
    voltage = 3.85
    check.that(voltage, ">=", 3.0)
    check.that(voltage, "<=", 4.25)


def test_voltage_settled(check):
    """`is_close` uses math.isclose defaults; `is_approximately`
    uses pytest.approx defaults — pick whichever matches your
    domain's expected-error model."""
    check.that(3.851, "is_close", 3.85, rel_tol=0.01)
    check.that(3.851, "is_approximately", 3.85)

Custom local-eval operators

For domain-specific predicates that don't fit the typed vocabulary, the zelos_sdk.pytest.checker.ops registry lets you register a DescriptiveOp and use it like any other op string. Custom ops are local-eval only — the callable runs in-process and the result posts to the board, but the spec never crosses the wire to the agent (so they pair with literal / cache-field operands, not Signal handles).

from zelos_sdk.pytest.checker import ops

def within_range(value, bounds):
    lo, hi = bounds
    return lo <= value <= hi

ops.register_op(ops.DescriptiveOp(within_range, "is within range"))

def test_range(check):
    check.that(3.85, "is within range", (3.0, 4.25))

Or pass a callable directly without registration (the function's __name__ becomes the board label):

def test_inline_predicate(check):
    check.that(5.0, lambda v, b: b[0] <= v <= b[1], (0, 10))

Typed ops always win the dispatch race — registering "==" as a custom op can't shadow the typed fast path. Custom ops paired with a Signal raise immediately (use the typed Op vocabulary for agent-side temporal evaluation instead). register_op(..., overwrite=True) replaces an existing entry; unregister_op("description") removes one.

Logging Integration

from zelos_sdk.hooks.logging import TraceLoggingHandler
import logging

# Add to any test
def test_with_logs():
    handler = TraceLoggingHandler(source_name="test_logs")
    logging.getLogger().addHandler(handler)

    logging.info("Test started")
    # All logs now appear in trace