A CLI test binary substitute with record and replay.
Commandeer allows you to record command-line invocations and their outputs during testing, then replay them later for deterministic test execution. This is particularly useful for:
- Testing CLI applications that invoke external commands
- Creating reproducible test environments
- Mocking system commands in integration tests
- Recording and replaying complex command interactions
This project is organized as a Cargo workspace with two crates:
commandeer-test- Core library providing record/replay functionalitycommandeer-macros- Procedural macros for test setup automation
Add commandeer to your Cargo.toml:
cargo add commandeer --devThe commandeer binary provides standalone record and replay functionality:
# Record a simple command
commandeer record --command echo hello world
# Record with custom storage file
commandeer record --file my-recordings.json --command ls -la# Replay a recorded command
commandeer replay --command echo hello world
# Replay from custom storage file
commandeer replay --file my-recordings.json --command ls -lause commandeer_test::{Commandeer, Mode};
use serial_test::serial;
#[test]
#[serial]
fn test_with_mocked_commands() {
let commandeer = Commandeer::new("my-test.json", Mode::Record);
// Mock specific commands to intercept them during test execution
commandeer.mock_command("git");
// Your test code that calls git/npm will now be recorded
let output = std::process::Command::new("git")
.args(&["status"])
.output()
.unwrap();
assert!(output.status.success());
}The #[commandeer] macro provides automatic test setup:
use commandeer_test::commandeer;
use serial_test::serial;
#[test]
#[commandeer(Record, "git", "npm", "curl")]
#[serial]
fn with_macro_test() {
// Macro roughly_expands_to:
// let commandeer = Commandeer::new("test_with_macro_test.json", Mode::Record);
// commandeer.mock_command("git");
// commandeer.mock_command("npm");
// commandeer.mock_command("curl");
let output = std::process::Command::new("git")
.args(&["--version"])
.output()
.unwrap();
assert!(output.status.success());
}
#[tokio::test]
#[commandeer(Replay, "git")]
#[serial]
async fn test_replay_with_macro() {
// Uses replay mode with the same automatic setup
let output = tokio::process::Command::new("git")
.arg("--version")
.output()
.await
.unwrap();
assert!(output.status.success());
}- Automatic file naming: Test file names are generated as
test_{function_name}.json - Mode selection: Supports both
RecordandReplaymodes - Command mocking: Automatically sets up mocks for specified commands
- Commandeer intercepts specified command invocations
- Executes the real commands and captures:
- Command name and arguments
- Standard output (stdout)
- Standard error (stderr)
- Exit code
- Stores results in JSON format for later replay
- Commandeer intercepts specified command invocations
- Looks up previous recordings based on command name and arguments
- Returns the stored stdout, stderr, and exit code
- Provides deterministic test execution without external dependencies
The library uses a sophisticated PATH manipulation system:
- Creates temporary mock binaries that intercept command calls
- Mock binaries delegate to the commandeer CLI for record/replay logic
- Original PATH is preserved and restored
- Works across different shell environments
Recordings are stored in JSON format:
{
"commands": {
"git:--version": [
{
"binary_name": "git",
"args": ["--version"],
"stdout": "git version 2.39.0\n",
"stderr": "",
"exit_code": 0
}
]
}
}# Build all crates
cargo build --workspace
# Build specific crate
cargo build -p commandeer-test# Run all tests
cargo test --workspace
# Run tests for specific crate
cargo test -p commandeer# Test record functionality
cargo run record --command echo "Hello, Commandeer"
# Test replay functionality
cargo run replay --command echo "Hello, Commandeer"#[test]
#[commandeer(Record, "docker", "kubectl")]
fn test_deployment_pipeline() {
// Test your deployment scripts that call docker and kubectl
deploy_application();
// Commands are recorded for later replay in CI
}#[test]
#[commandeer(Replay, "git", "ssh")]
fn test_git_operations() {
// Test git workflows without requiring actual git repository
run_git_workflow();
}#[test]
#[commandeer(Record, "curl", "wget")]
fn test_api_interactions() {
// Record API calls during development
// Replay them in tests for consistent behavior
fetch_external_data();
}Due to how commandeer utilizes the $PATH of a process, only relative binary invocations are supported (absolute path binary invocations do not work).
Licensed under the MIT License. See LICENSE file for details.
Contributions are welcome! Please ensure tests pass and follow the existing code style.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request