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 --dev
The 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 -la
use 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
Record
andReplay
modes - 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();
}
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