cli_tester
cli_tester
Crystal testing utility for CLI applications. Provides isolated environments and tools to test command-line interactions.
Features
- ๐ Shard Validation
- Automatic shard.yml detection
- Configuration parsing with YAML validation
- Required field checking (name, targets)
- Precise error locations in YAML files
- ๐ฅ๏ธ XDG Compliance Testing
- ๐ Environment Isolation
- ๐งน Output Normalization
- ๐ธ Snapshot Testing
- ๐ค Interactive Testing
- โก Mock Adapters
Installation
Add to shard.yml
:
dependencies:
cli_tester:
github: dsisnero/cli_tester
Then run:
shards install
Usage
require "cli_tester"
describe "MyCLI" do
it "tests CLI behavior" do
CliTester.test do |env|
# XDG Config Testing
env.create_xdg_config("myapp", "config.yml", "debug: true")
# Temporary ENV variables
env.with_temp_env({"APP_MODE" => "test"}) do
# XDG-aware command execution
# Ensure XDG_CONFIG_HOME is set for the command
config_home = env.env["XDG_CONFIG_HOME"]
cmd = CliTester::Shell.xdg_command("myapp --verbose", config_home)
result = env.execute(cmd)
result.stdout.should contain("Debug mode enabled")
# Example: Check cache file created by the app
# env.read_file("#{env.env["XDG_CACHE_HOME"]}/myapp/cache.dat").should eq("cached")
end
# Test interactive prompts
process = env.spawn("my_cli --interactive")
process.wait_for_text("Enter name:")
process.write_text("Tester")
process.wait_for_finish
process.stdout.should contain("Hello Tester")
end
end
end
Shard Validation
Test your shard.yml configuration:
it "validates shard configuration" do
# Get parsed shard configuration
shard = CliTester::Shard.parse(File.read(CliTester.shard_file))
shard.name.should eq "my_cli"
shard.targets["main"].main.should eq "src/main.cr"
# Test invalid configurations
invalid_yaml = <<-YAML
version: 1.0
targets:
broken: {}
YAML
expect_raises(YAML::ParseException, /Missing required 'name' field/) do
CliTester::Shard.parse(invalid_yaml)
end
end
XDG Environment Testing
Test XDG-compliant applications without touching real configs:
it "uses XDG config locations" do
CliTester.test do |env|
# Create test config
env.create_xdg_config("myapp", "settings.toml", <<-TOML
[features]
experimental = true
TOML
)
# Verify config location
config_path = File.join(env.env["XDG_CONFIG_HOME"], "myapp/settings.toml")
File.exists?(config_path).should be_true
# Test CLI behavior using XDG-aware command
cmd = CliTester::Shell.xdg_command("myapp show-config", env.env["XDG_CONFIG_HOME"])
result = env.execute(cmd)
result.stdout.should contain("experimental: true")
end
end
Environment Variable Management
Safely test environment-dependent behavior:
it "respects APP_DEBUG flag" do
CliTester.test do |env|
env.with_temp_env({
"APP_DEBUG" => "1",
"OLD_VAR" => nil # Unset during test
}) do
result = env.execute("myapp")
result.stdout.should contain("[DEBUG]")
end
end
end
Output Normalization Examples
# Raw output contains color codes and paths:
"Processing \e[32m/tmp/cli-test-xyz/file.txt\e[0m"
# Normalized output becomes:
"Processing {base}/file.txt"
Snapshot Testing
Update snapshots by:
- Delete the snapshot file
- Re-run tests - new snapshot will be generated
CliTester::Snapshot.assert_match_snapshot(
result.normalized_stdout(env),
"spec/snapshots/main_output.txt"
)
Mock Adapters
Example mocking HTTP calls:
class MockSuccessAPI < CliTester::MockAdapter
def apply_mocks
MyHTTPClient.stub(:get, "https://api.example.com") do
HTTP::Client::Response.new(200, "{\"status\":\"ok\"}")
end
end
end
env.with_mocks(MockSuccessAPI.new) do
result = env.execute("my-cli fetch-data")
# Assert against mocked response
end
Interactive Testing Flow
process = env.spawn("my-cli setup")
process.wait_for_text("Enter API key:")
process.write_text("test-key-123")
process.wait_for_text("Validating...", stream: :stderr)
process.wait_for_finish
process.get_stdout.should contain("Setup complete")
Development Notes
# Run tests with output normalization:
CRYSTAL_LOG_LEVEL=DEBUG crystal spec
# Generate test coverage:
crystal spec --error-trace --debug -Dpreview_mt
Roadmap
- Automated snapshot updates via env var
- Windows path normalization
- ANSI progress bar handling
- Multi-process concurrency testing
Contributing
- Fork it (https://github.com/dsisnero/cli_tester/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Contributors
- Dominic Sisneros - creator and maintainer
Repository
cli_tester
Owner
Statistic
- 0
- 0
- 0
- 0
- 0
- 9 days ago
- April 29, 2025
License
MIT License
Links
Synced at
Thu, 08 May 2025 19:06:43 GMT
Languages