Command Mock Framework
High-fidelity, deterministic command mocking for Python tests.
Stop mocking subprocess.run with invented strings. Record real command outputs once, store them in version control, and replay them during tests.
The Problem
Testing code that wraps CLI tools (Git, Docker, Kubectl, npm) is painful.
- Manual mocks are brittle: You guess what
git logreturns, but you get it wrong. - Real commands are slow: Running
docker psin every test kills performance. - Integration tests are flaky: Environments change, causing non-deterministic failures.
The Solution
Command Mock Framework is a VCR-style library for subprocess.run. It sits between your code and subprocess.
- Record real command outputs to TOML files.
- Replay them instantly in tests.
- Match commands flexibly using template placeholders.
Easily mock complex tools like AWS CLI, Kubectl, Terraform, and Git without manually constructing stdout strings.
Quick Example
1. Record the real behavior:
from command_mock.recorder import CommandMockRecorder
# Run this once to generate 'mocks/git/log/follow.toml'
recorder = CommandMockRecorder("git")
recorder.record_scenario(
command=["git", "log", "--follow", "--", "{filepath}"],
scenario_name="basic_history",
template_vars={"filepath": "app.py"}
)
2. Use it in your test:
def test_get_history(command_mock):
# Load the mock (command_mock is a pytest fixture)
mock_fn = command_mock.get_subprocess_mock("log/follow.toml", "basic_history")
# Patch subprocess.run
with patch('subprocess.run', side_effect=mock_fn):
# This executes FAST and returns REAL git output
# It matches 'git log ... -- app.py' OR 'git log ... -- other.py'
history = my_git_wrapper.get_history("app.py")
assert len(history) == 5
Alternatives & Philosophy
The Command Mock Framework occupies a specific niche: it applies the VCR/Record-Replay pattern (popularized by HTTP tools like vcrpy) to subprocess/CLI calls.
Stop using unittest.mock.patch with fake stdout strings. This framework guarantees fidelity by recording reality first.
Here is how it compares to other tools in the Python ecosystem:
1. The Direct Competitor: pytest-subprocess
This is the most popular modern library for this task. It allows you to define expected commands and outputs inside your test code.
- pytest-subprocess: Best for Code-Driven testing. Excellent for defining logic flows (e.g., "if command X runs, return exit code 1").
- command-mock: Best for Data-Driven testing. It separates test data (TOML) from test logic. It shines when output is verbose or structured (like
git logordocker inspect) and you want to version-control the exact output snapshot.
2. The Standard Library: unittest.mock
The "vanilla" way that this framework was built to replace.
- unittest.mock: Often leads to brittle tests. You have to manually invent stdout strings, which means your tests might pass while the real app fails because your invented string wasn't quite right.
- command-mock: Guarantees fidelity by recording reality first.
3. "Real Execution" Tools (cram, scripttest)
These tools run actual shell commands in a sandbox.
- cram: Provides 100% realism but is slow and requires a full environment setup (installing git, docker, etc. on the test runner).
- command-mock: Replays instantly (
⚡ Fast) and requires no external tools installed in the CI environment.
4. The HTTP Equivalent: vcrpy
If your CLI tool is primarily a wrapper around an API (e.g., a custom AWS wrapper), you might be mocking the wrong layer.
- Strategy: Instead of mocking the subprocess call to
aws-cli, use the Python SDK (boto3) and usevcrpyto record the HTTP interactions. Use command-mock when you must shell out to a binary.
Summary
| Feature | command-mock | pytest-subprocess | unittest.mock | cram |
|---|---|---|---|---|
| Primary Goal | High-fidelity Replay | Programmatic Logic | Basic Mocking | Integration Testing |
| Data Storage | External Files (TOML) | In Python Code | In Python Code | Shell Transcript Files |
| Realism | ⭐⭐⭐⭐ (Recorded) | ⭐⭐ (Manual) | ⭐ (Manual) | ⭐⭐⭐⭐⭐ (Real) |
| Speed | ⚡ Fast | ⚡ Fast | ⚡ Fast | 🐢 Slow |
| Best For | Complex outputs | Logic flows, exit codes | Simple commands | End-to-end flows |
Conclusion: If you need to mock complex tools like Git, Docker, or Kubectl where the stdout is verbose and structured, command-mock is the best fit because managing those massive strings inside Python code is messy and error-prone.
Installation
pip install orgecc-command-mock
Usage Guide
1. Setup
Add the pytest plugin to your conftest.py:
# tests/conftest.py
import pytest
from pathlib import Path
from command_mock.recorder import CommandMockRecorder
from command_mock.player import CommandMockPlayer
@pytest.fixture
def command_mock(request):
"""Fixture that switches between Player (default) and Recorder."""
# Point this to where you want to store your TOML files
fixtures_root = Path(__file__).parent
if request.config.getoption("--regenerate-mocks"):
return CommandMockRecorder("git", fixtures_root=fixtures_root)
else:
return CommandMockPlayer("git", fixtures_root=fixtures_root)
def pytest_addoption(parser):
parser.addoption("--regenerate-mocks", action="store_true", help="Record new mocks")
2. Recording Mocks
Create a script (e.g., tests/generate_mocks.py) to generate your test data. This ensures your mocks are reproducible.
from command_mock.recorder import CommandMockRecorder
def generate():
recorder = CommandMockRecorder("git")
# You can even use setup scripts to create real repo states!
repo = recorder.create_test_repo("scripts/setup_repo.sh")
recorder.record_scenario(
command=["git", "status"],
scenario_name="clean_state",
output_path="status/clean.toml",
repo_path=repo
)
if __name__ == "__main__":
generate()
3. Flexible Matching
This is the framework's superpower. You don't need a mock for every single argument variation. Use placeholders in your templates.
Embedded Placeholders:
# Template in TOML:
command = ["git", "log", "--grep={term}"]
# Matches all of these in tests:
["git", "log", "--grep=fix"]
["git", "log", "--grep=feat"]
Standalone Placeholders:
# Template in TOML:
command = ["git", "add", "{filepath}"]
# Matches all of these in tests:
["git", "add", "src/main.py"]
["git", "add", "tests/test_core.py"]
Dynamic Flag Stripping:
The player automatically handles flags that change every run (like --since="1 hour ago"), matching the core command while ignoring the dynamic parts.
Real-World Example: Hug SCM
This framework was extracted from Hug SCM, a humane Git interface. Hug SCM relies on this library to test its complex Git history analysis features without needing a Git repo in CI.
You can explore Hug SCM's source code to see the framework in action:
| Component | File in Hug SCM | Purpose |
|---|---|---|
| Integration | tests/conftest.py |
Shows how to configure the command_mock fixture to point to a local fixtures/ directory. |
| Recording | tests/fixtures/generate_mocks.py |
A robust script that generates 50+ mocks for git log, churn, and activity analysis. Demonstrates batch recording. |
| Data | tests/fixtures/mocks/git/log/ |
See how real TOML mock files are structured and organized by command. |
| Tests | tests/test_activity.py |
See how tests consume these mocks to validate complex logic like "weekend work detection". |
Development
This project uses a Makefile for all development tasks.
make install # Install dev dependencies
make test # Run tests
make format # Format code (Black/Ruff)
make ci # Run full CI suite locally
For detailed contribution guidelines, see CLAUDE.md.
License
Apache 2.0
Metadata
Release files for orgecc-command-mock 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| orgecc_command_mock-0.1.6.tar.gz | 30.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| orgecc_command_mock-0.1.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.6 kB
Release files / orgecc_command_mock-0.1.6.tar.gz
| Download URL | orgecc_command_mock-0.1.6.tar.gz |
|---|---|
| Size | 30.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6cc8f058f798a1f7bb1505c09ca4c0438916b66f948d746b51c33334ee8d902
|
|
BLAKE2b-256 checksum How to use checksums |
ddf3bec5e516b44f2fce4fde7b06fede106ea78726ad65da3a8747b0b46727c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 26, 2025.
Transparency logRelease files / orgecc_command_mock-0.1.6-py3-none-any.whl
| Download URL | orgecc_command_mock-0.1.6-py3-none-any.whl |
|---|---|
| Size | 13.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
655823d2d31c218549d272ec5f6c81c9e9473b1b2265a3927799b3c486bbdf53
|
|
BLAKE2b-256 checksum How to use checksums |
bc0bfec47c538618d38d5fd709a07f0cff4c5ef41d26f04e1b7128812e28fad1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 26, 2025.
Transparency logSecurity for orgecc-command-mock 0.1.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for orgecc_command_mock-0.1.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 26, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.