name: "agentic-jumpstart-testing" description: "Testing guidelines for Jarvy CLI - unit testing patterns, integration tests with assert_cmd, test environment variables, platform-specific testing, and CI coverage strategies."
Testing Guidelines
This skill provides testing patterns and strategies for the Jarvy CLI project.
Test Organization
Unit Tests Location
Unit tests live alongside code:
// src/tools/git/git.rs
pub fn ensure(min_hint: &str) -> Result<(), InstallError> {
// Implementation
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ensure_parses_version() {
// Test implementation
}
}
Integration Tests Location
tests/
├── cli_dispatch.rs # Command routing
├── cli_config_errors.rs # Config error handling
├── tools_install.rs # Tool installation
└── ...
Environment Variables
JARVY_TEST_MODE=1
Disables interactive prompts:
#[test]
fn test_setup() {
Command::cargo_bin("jarvy")
.unwrap()
.env("JARVY_TEST_MODE", "1")
.arg("setup")
.assert()
.success();
}
JARVY_FAST_TEST
Skips external command execution:
#[test]
fn test_config_parsing() {
Command::cargo_bin("jarvy")
.unwrap()
.env("JARVY_FAST_TEST", "1")
.env("JARVY_TEST_MODE", "1")
.arg("setup")
.assert()
.success();
}
JARVY_RUN_EXTERNAL_CMDS_IN_TEST
Enables external commands in unit tests (use sparingly):
#[test]
#[ignore]
fn test_actual_installation() {
std::env::set_var("JARVY_RUN_EXTERNAL_CMDS_IN_TEST", "1");
// Test that calls real commands
}
Test Utilities
make_config() Helper
use tempfile::TempDir;
use std::fs;
fn make_config(content: &str) -> TempDir {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("jarvy.toml");
fs::write(&config_path, content).unwrap();
temp_dir
}
#[test]
fn test_simple_config() {
let temp = make_config(r#"
[provisioner]
git = "2.40"
"#);
Command::cargo_bin("jarvy")
.unwrap()
.env("JARVY_TEST_MODE", "1")
.env("JARVY_FAST_TEST", "1")
.arg("setup")
.current_dir(temp.path())
.assert()
.success();
}
Integration Testing with assert_cmd
Basic CLI Invocation
use assert_cmd::Command;
use predicates::prelude::*;
#[test]
fn test_help() {
Command::cargo_bin("jarvy")
.unwrap()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("Usage:"));
}
Testing Exit Codes
#[test]
fn test_config_error() {
let temp = make_config("invalid toml {{{{");
Command::cargo_bin("jarvy")
.unwrap()
.env("JARVY_TEST_MODE", "1")
.arg("setup")
.current_dir(temp.path())
.assert()
.code(2); // CONFIG_ERROR
}
Predicate Assertions
use predicates::str::{contains, is_empty};
#[test]
fn test_output() {
Command::cargo_bin("jarvy")
.unwrap()
.env("JARVY_TEST_MODE", "1")
.arg("get")
.assert()
.success()
.stdout(contains("tools"));
}
Platform-Specific Testing
Conditional Test Compilation
#[test]
#[cfg(target_os = "macos")]
fn test_homebrew() {
// macOS-specific test
}
#[test]
#[cfg(target_os = "linux")]
fn test_apt() {
// Linux-specific test
}
#[test]
#[cfg(target_os = "windows")]
fn test_winget() {
// Windows-specific test
}
Platform Test Modules
#[cfg(test)]
mod tests {
#[cfg(target_os = "macos")]
mod macos_tests {
#[test]
fn test_brew_install() { }
}
#[cfg(target_os = "linux")]
mod linux_tests {
#[test]
fn test_apt_install() { }
}
}
Unit Testing Patterns
Testing Tool Handlers
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_parsing() {
assert!(version_satisfies("2.40.0", "2.40"));
assert!(!version_satisfies("2.39.0", "2.40"));
}
}
Testing Config Parsing
#[test]
fn test_simple_format() {
let config = r#"
[provisioner]
git = "2.40"
"#;
let parsed: Config = toml::from_str(config).unwrap();
assert_eq!(parsed.tools.get("git").unwrap().version, "2.40");
}
#[test]
fn test_detailed_format() {
let config = r#"
[provisioner]
node = { version = "20.0", version_manager = true }
"#;
let parsed: Config = toml::from_str(config).unwrap();
let node = parsed.tools.get("node").unwrap();
assert!(node.version_manager);
}
CI Configuration
GitHub Actions
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --verbose -- --show-output
env:
JARVY_TEST_MODE: "1"
JARVY_FAST_TEST: "1"
Coverage
cargo install cargo-llvm-cov
cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
Test Categories
// Fast tests (default)
#[test]
fn test_config_parsing() { }
// Slow tests (run with --ignored)
#[test]
#[ignore]
fn test_actual_installation() { }
// Platform-specific
#[test]
#[cfg(target_os = "macos")]
fn test_homebrew() { }
Running Tests
# All tests
cargo test --verbose -- --show-output
# Single test file
cargo test --test cli_dispatch -- --show-output
# Tests matching pattern
cargo test config -- --show-output
# Ignored (slow) tests
cargo test -- --ignored
# All including ignored
cargo test -- --include-ignored
Best Practices
- Always set JARVY_TEST_MODE=1 to prevent interactive prompts
- Use JARVY_FAST_TEST when not testing actual installation
- Keep unit tests fast by mocking external commands
- Use platform-specific modules with
#[cfg(target_os)] - Test error paths - verify exit codes and messages
- Use tempfile for isolation - temporary config files
- Mark slow tests with #[ignore]
- Test both config formats - simple and detailed