CLI integration tests
CLI tests verify the compiled binary’s behavior end-to-end: argument parsing, output formatting,
exit codes, and filesystem interactions. They live in a tests/ directory at the crate root and
compile the binary rather than calling functions directly.
Organization
CLI tests are not organized by source module. Modules within the tests/ directory create the
naming hierarchy:
- Environment dependency — Tests that require API tokens are grouped by their credential
requirement. Files named after the command they test have no external dependency. Examples:
github(requiresGITHUB_TOKEN),graphos(requiresAPOLLO_KEY). - Command — The CLI subcommand under test. Examples:
template,run,remote. Where no command is supplied, useno_command. - Flag(s) — Flags that cause significant branching in functionality, added as a prefix to the test case name. Omit if the flag is already implied by the environment dependency.
- Test class — A grouping of parameterized cases defined with
simple_test_case. - Test case — The specific scenario, uniquely and meaningfully named. See test case naming.
Following the hierarchy above leads to the following generic test case paths:
#![allow(unused)]
fn main() {
// Without simple_test_case
env_dependency::command::flags_test_case
// With simple_test_case
env_dependency::command::flags_test_class::test_case
}
Testing infrastructure
CLI tests use the following tools:
assert_cmd— Spawns the compiled binary and asserts on stdout, stderr, and exit statusassert_fs— Temporary filesystem utilities for managing test directoriespredicates— Composable assertion predicates for output matchingindoc— Clean multi-line expected output stringssimple_test_case— Parameterized testing across multiple input variationscargo_bin_cmd!— Macro that builds a command for the target binary
Ignoring credential-dependent tests
Tests that require API tokens must be ignored by default. The #[ignore] attribute takes a reason
string explaining which credential is needed:
#![allow(unused)]
fn main() {
#[test]
#[ignore = "requires a valid GitHub API Token"]
fn github_flag_completes() {
// ...
}
}
This keeps cargo test fast and dependency-free. To run these tests, supply the credential as an
environment variable and pass -- --ignored or -- --include-ignored to cargo.
Temporary directory management
CmdWithTmpDir combines a command with a temporary directory, ensuring filesystem state is cleaned
up after each test:
#![allow(unused)]
fn main() {
pub struct CmdWithTmpDir {
cmd: Command,
tmp: TempDir,
}
}
Use this when a test must write output files and the test needs to inspect them.
Test resources
Test data lives under resources/ at the crate root, organized by resource type and validity:
resources/
└── <resource_type>/
├── valid/ — complete, valid test plans for success scenarios
└── invalid/ — organized by failure category (load-and-resolve, templating, checks, run)
Each test plan directory is self-contained with all necessary files.