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.