Spies and passthrough mode

Version 0.2.0 Updated Nov 27, 2025

Spies expose invocations (a list of Invocation objects) and call_count during and after replay, making it easy to inspect what actually ran:

def test_spy(cmd_mox):
    spy = cmd_mox.spy("curl").returns(stdout="ok")
    run_download()
    assert spy.call_count == 1

A spy expectation can also use times_called(count)—an alias of times(count)—to require a specific call count during verification.

A spy can also forward to the real command while recording everything:

mox.spy("aws").passthrough()

This "record mode" is helpful for capturing real interactions and later turning them into mocks. During passthrough, the IPC server sends the shim a PassthroughRequest containing the original PATH and any expectation-specific environment overrides. The shim resolves and runs the real command, then reports the captured stdout, stderr, and exit_code back to the server before the call returns. The calling process therefore observes the genuine behaviour while CmdMox records the interaction for later assertions.

For integration tests that need deterministic control over which executable a passthrough spy invokes, set CMOX_REAL_COMMAND_<NAME> in the shim environment. When present, the shim bypasses the PATH lookup and executes the absolute path specified by the variable. This override is intended solely for tests—production scenarios should allow the shim to resolve commands from the original PATH to avoid masking misconfigurations.

Spies provide assertion helpers inspired by unittest.mock that can be called in the test body or after verification:

spy.assert_called()
spy.assert_called_with("--silent", stdin="payload")
# or, to ensure the spy never executed:
spy.assert_not_called()

These methods raise AssertionError when expectations are not met and are restricted to spy doubles.