| # Command Testing Strategies |
|
|
| Comprehensive strategies for testing slash commands before deployment and distribution. |
|
|
| ## Overview |
|
|
| Testing commands ensures they work correctly, handle edge cases, and provide good user experience. A systematic testing approach catches issues early and builds confidence in command reliability. |
|
|
| ## Testing Levels |
|
|
| ### Level 1: Syntax and Structure Validation |
|
|
| **What to test:** |
| - YAML frontmatter syntax |
| - Markdown format |
| - File location and naming |
|
|
| **How to test:** |
|
|
| ```bash |
| # Validate YAML frontmatter |
| head -n 20 .claude/commands/my-command.md | grep -A 10 "^---" |
| |
| # Check for closing frontmatter marker |
| head -n 20 .claude/commands/my-command.md | grep -c "^---" # Should be 2 |
| |
| # Verify file has .md extension |
| ls .claude/commands/*.md |
| |
| # Check file is in correct location |
| test -f .claude/commands/my-command.md && echo "Found" || echo "Missing" |
| ``` |
|
|
| **Automated validation script:** |
|
|
| ```bash |
| #!/bin/bash |
| # validate-command.sh |
| |
| COMMAND_FILE="$1" |
| |
| if [ ! -f "$COMMAND_FILE" ]; then |
| echo "ERROR: File not found: $COMMAND_FILE" |
| exit 1 |
| fi |
| |
| # Check .md extension |
| if [[ ! "$COMMAND_FILE" =~ \.md$ ]]; then |
| echo "ERROR: File must have .md extension" |
| exit 1 |
| fi |
| |
| # Validate YAML frontmatter if present |
| if head -n 1 "$COMMAND_FILE" | grep -q "^---"; then |
| # Count frontmatter markers |
| MARKERS=$(head -n 50 "$COMMAND_FILE" | grep -c "^---") |
| if [ "$MARKERS" -ne 2 ]; then |
| echo "ERROR: Invalid YAML frontmatter (need exactly 2 '---' markers)" |
| exit 1 |
| fi |
| echo "✓ YAML frontmatter syntax valid" |
| fi |
| |
| # Check for empty file |
| if [ ! -s "$COMMAND_FILE" ]; then |
| echo "ERROR: File is empty" |
| exit 1 |
| fi |
| |
| echo "✓ Command file structure valid" |
| ``` |
|
|
| ### Level 2: Frontmatter Field Validation |
|
|
| **What to test:** |
| - Field types correct |
| - Values in valid ranges |
| - Required fields present (if any) |
|
|
| **Validation script:** |
|
|
| ```bash |
| #!/bin/bash |
| # validate-frontmatter.sh |
| |
| COMMAND_FILE="$1" |
| |
| # Extract YAML frontmatter |
| FRONTMATTER=$(sed -n '/^---$/,/^---$/p' "$COMMAND_FILE" | sed '1d;$d') |
| |
| if [ -z "$FRONTMATTER" ]; then |
| echo "No frontmatter to validate" |
| exit 0 |
| fi |
| |
| # Check 'model' field if present |
| if echo "$FRONTMATTER" | grep -q "^model:"; then |
| MODEL=$(echo "$FRONTMATTER" | grep "^model:" | cut -d: -f2 | tr -d ' ') |
| if ! echo "sonnet opus haiku" | grep -qw "$MODEL"; then |
| echo "ERROR: Invalid model '$MODEL' (must be sonnet, opus, or haiku)" |
| exit 1 |
| fi |
| echo "✓ Model field valid: $MODEL" |
| fi |
| |
| # Check 'allowed-tools' field format |
| if echo "$FRONTMATTER" | grep -q "^allowed-tools:"; then |
| echo "✓ allowed-tools field present" |
| # Could add more sophisticated validation here |
| fi |
| |
| # Check 'description' length |
| if echo "$FRONTMATTER" | grep -q "^description:"; then |
| DESC=$(echo "$FRONTMATTER" | grep "^description:" | cut -d: -f2-) |
| LENGTH=${#DESC} |
| if [ "$LENGTH" -gt 80 ]; then |
| echo "WARNING: Description length $LENGTH (recommend < 60 chars)" |
| else |
| echo "✓ Description length acceptable: $LENGTH chars" |
| fi |
| fi |
| |
| echo "✓ Frontmatter fields valid" |
| ``` |
|
|
| ### Level 3: Manual Command Invocation |
|
|
| **What to test:** |
| - Command appears in `/help` |
| - Command executes without errors |
| - Output is as expected |
|
|
| **Test procedure:** |
|
|
| ```bash |
| # 1. Start Claude Code |
| claude --debug |
| |
| # 2. Check command appears in help |
| > /help |
| # Look for your command in the list |
| |
| # 3. Invoke command without arguments |
| > /my-command |
| # Check for reasonable error or behavior |
| |
| # 4. Invoke with valid arguments |
| > /my-command arg1 arg2 |
| # Verify expected behavior |
| |
| # 5. Check debug logs |
| tail -f ~/.claude/debug-logs/latest |
| # Look for errors or warnings |
| ``` |
|
|
| ### Level 4: Argument Testing |
|
|
| **What to test:** |
| - Positional arguments work ($1, $2, etc.) |
| - $ARGUMENTS captures all arguments |
| - Missing arguments handled gracefully |
| - Invalid arguments detected |
|
|
| **Test matrix:** |
|
|
| | Test Case | Command | Expected Result | |
| |-----------|---------|-----------------| |
| | No args | `/cmd` | Graceful handling or useful message | |
| | One arg | `/cmd arg1` | $1 substituted correctly | |
| | Two args | `/cmd arg1 arg2` | $1 and $2 substituted | |
| | Extra args | `/cmd a b c d` | All captured or extras ignored appropriately | |
| | Special chars | `/cmd "arg with spaces"` | Quotes handled correctly | |
| | Empty arg | `/cmd ""` | Empty string handled | |
|
|
| **Test script:** |
|
|
| ```bash |
| #!/bin/bash |
| # test-command-arguments.sh |
| |
| COMMAND="$1" |
| |
| echo "Testing argument handling for /$COMMAND" |
| echo |
| |
| echo "Test 1: No arguments" |
| echo " Command: /$COMMAND" |
| echo " Expected: [describe expected behavior]" |
| echo " Manual test required" |
| echo |
| |
| echo "Test 2: Single argument" |
| echo " Command: /$COMMAND test-value" |
| echo " Expected: 'test-value' appears in output" |
| echo " Manual test required" |
| echo |
| |
| echo "Test 3: Multiple arguments" |
| echo " Command: /$COMMAND arg1 arg2 arg3" |
| echo " Expected: All arguments used appropriately" |
| echo " Manual test required" |
| echo |
| |
| echo "Test 4: Special characters" |
| echo " Command: /$COMMAND \"value with spaces\"" |
| echo " Expected: Entire phrase captured" |
| echo " Manual test required" |
| ``` |
|
|
| ### Level 5: File Reference Testing |
|
|
| **What to test:** |
| - @ syntax loads file contents |
| - Non-existent files handled |
| - Large files handled appropriately |
| - Multiple file references work |
|
|
| **Test procedure:** |
|
|
| ```bash |
| # Create test files |
| echo "Test content" > /tmp/test-file.txt |
| echo "Second file" > /tmp/test-file-2.txt |
| |
| # Test single file reference |
| > /my-command /tmp/test-file.txt |
| # Verify file content is read |
| |
| # Test non-existent file |
| > /my-command /tmp/nonexistent.txt |
| # Verify graceful error handling |
| |
| # Test multiple files |
| > /my-command /tmp/test-file.txt /tmp/test-file-2.txt |
| # Verify both files processed |
| |
| # Test large file |
| dd if=/dev/zero of=/tmp/large-file.bin bs=1M count=100 |
| > /my-command /tmp/large-file.bin |
| # Verify reasonable behavior (may truncate or warn) |
| |
| # Cleanup |
| rm /tmp/test-file*.txt /tmp/large-file.bin |
| ``` |
|
|
| ### Level 6: Bash Execution Testing |
|
|
| **What to test:** |
| - !` commands execute correctly |
| - Command output included in prompt |
| - Command failures handled |
| - Security: only allowed commands run |
|
|
| **Test procedure:** |
|
|
| ```bash |
| # Create test command with bash execution |
| cat > .claude/commands/test-bash.md << 'EOF' |
| --- |
| description: Test bash execution |
| allowed-tools: Bash(echo:*), Bash(date:*) |
| --- |
| |
| Current date: !`date` |
| Test output: !`echo "Hello from bash"` |
| |
| Analysis of output above... |
| EOF |
| |
| # Test in Claude Code |
| > /test-bash |
| # Verify: |
| # 1. Date appears correctly |
| # 2. Echo output appears |
| # 3. No errors in debug logs |
| |
| # Test with disallowed command (should fail or be blocked) |
| cat > .claude/commands/test-forbidden.md << 'EOF' |
| --- |
| description: Test forbidden command |
| allowed-tools: Bash(echo:*) |
| --- |
| |
| Trying forbidden: !`ls -la /` |
| EOF |
| |
| > /test-forbidden |
| # Verify: Permission denied or appropriate error |
| ``` |
|
|
| ### Level 7: Integration Testing |
|
|
| **What to test:** |
| - Commands work with other plugin components |
| - Commands interact correctly with each other |
| - State management works across invocations |
| - Workflow commands execute in sequence |
|
|
| **Test scenarios:** |
|
|
| **Scenario 1: Command + Hook Integration** |
|
|
| ```bash |
| # Setup: Command that triggers a hook |
| # Test: Invoke command, verify hook executes |
| |
| # Command: .claude/commands/risky-operation.md |
| # Hook: PreToolUse that validates the operation |
| |
| > /risky-operation |
| # Verify: Hook executes and validates before command completes |
| ``` |
|
|
| **Scenario 2: Command Sequence** |
|
|
| ```bash |
| # Setup: Multi-command workflow |
| > /workflow-init |
| # Verify: State file created |
| |
| > /workflow-step2 |
| # Verify: State file read, step 2 executes |
| |
| > /workflow-complete |
| # Verify: State file cleaned up |
| ``` |
|
|
| **Scenario 3: Command + MCP Integration** |
|
|
| ```bash |
| # Setup: Command uses MCP tools |
| # Test: Verify MCP server accessible |
| |
| > /mcp-command |
| # Verify: |
| # 1. MCP server starts (if stdio) |
| # 2. Tool calls succeed |
| # 3. Results included in output |
| ``` |
|
|
| ## Automated Testing Approaches |
|
|
| ### Command Test Suite |
|
|
| Create a test suite script: |
|
|
| ```bash |
| #!/bin/bash |
| # test-commands.sh - Command test suite |
| |
| TEST_DIR=".claude/commands" |
| FAILED_TESTS=0 |
| |
| echo "Command Test Suite" |
| echo "==================" |
| echo |
| |
| for cmd_file in "$TEST_DIR"/*.md; do |
| cmd_name=$(basename "$cmd_file" .md) |
| echo "Testing: $cmd_name" |
| |
| # Validate structure |
| if ./validate-command.sh "$cmd_file"; then |
| echo " ✓ Structure valid" |
| else |
| echo " ✗ Structure invalid" |
| ((FAILED_TESTS++)) |
| fi |
| |
| # Validate frontmatter |
| if ./validate-frontmatter.sh "$cmd_file"; then |
| echo " ✓ Frontmatter valid" |
| else |
| echo " ✗ Frontmatter invalid" |
| ((FAILED_TESTS++)) |
| fi |
| |
| echo |
| done |
| |
| echo "==================" |
| echo "Tests complete" |
| echo "Failed: $FAILED_TESTS" |
| |
| exit $FAILED_TESTS |
| ``` |
|
|
| ### Pre-Commit Hook |
|
|
| Validate commands before committing: |
|
|
| ```bash |
| #!/bin/bash |
| # .git/hooks/pre-commit |
| |
| echo "Validating commands..." |
| |
| COMMANDS_CHANGED=$(git diff --cached --name-only | grep "\.claude/commands/.*\.md") |
| |
| if [ -z "$COMMANDS_CHANGED" ]; then |
| echo "No commands changed" |
| exit 0 |
| fi |
| |
| for cmd in $COMMANDS_CHANGED; do |
| echo "Checking: $cmd" |
| |
| if ! ./scripts/validate-command.sh "$cmd"; then |
| echo "ERROR: Command validation failed: $cmd" |
| exit 1 |
| fi |
| done |
| |
| echo "✓ All commands valid" |
| ``` |
|
|
| ### Continuous Testing |
|
|
| Test commands in CI/CD: |
|
|
| ```yaml |
| # .github/workflows/test-commands.yml |
| name: Test Commands |
| |
| on: [push, pull_request] |
| |
| jobs: |
| test: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v2 |
| |
| - name: Validate command structure |
| run: | |
| for cmd in .claude/commands/*.md; do |
| echo "Testing: $cmd" |
| ./scripts/validate-command.sh "$cmd" |
| done |
| |
| - name: Validate frontmatter |
| run: | |
| for cmd in .claude/commands/*.md; do |
| ./scripts/validate-frontmatter.sh "$cmd" |
| done |
| |
| - name: Check for TODOs |
| run: | |
| if grep -r "TODO" .claude/commands/; then |
| echo "ERROR: TODOs found in commands" |
| exit 1 |
| fi |
| ``` |
|
|
| ## Edge Case Testing |
|
|
| ### Test Edge Cases |
|
|
| **Empty arguments:** |
| ```bash |
| > /cmd "" |
| > /cmd '' '' |
| ``` |
|
|
| **Special characters:** |
| ```bash |
| > /cmd "arg with spaces" |
| > /cmd arg-with-dashes |
| > /cmd arg_with_underscores |
| > /cmd arg/with/slashes |
| > /cmd 'arg with "quotes"' |
| ``` |
|
|
| **Long arguments:** |
| ```bash |
| > /cmd $(python -c "print('a' * 10000)") |
| ``` |
|
|
| **Unusual file paths:** |
| ```bash |
| > /cmd ./file |
| > /cmd ../file |
| > /cmd ~/file |
| > /cmd "/path with spaces/file" |
| ``` |
|
|
| **Bash command edge cases:** |
| ```markdown |
| # Commands that might fail |
| !`exit 1` |
| !`false` |
| !`command-that-does-not-exist` |
| |
| # Commands with special output |
| !`echo ""` |
| !`cat /dev/null` |
| !`yes | head -n 1000000` |
| ``` |
|
|
| ## Performance Testing |
|
|
| ### Response Time Testing |
|
|
| ```bash |
| #!/bin/bash |
| # test-command-performance.sh |
| |
| COMMAND="$1" |
| |
| echo "Testing performance of /$COMMAND" |
| echo |
| |
| for i in {1..5}; do |
| echo "Run $i:" |
| START=$(date +%s%N) |
| |
| # Invoke command (manual step - record time) |
| echo " Invoke: /$COMMAND" |
| echo " Start time: $START" |
| echo " (Record end time manually)" |
| echo |
| done |
| |
| echo "Analyze results:" |
| echo " - Average response time" |
| echo " - Variance" |
| echo " - Acceptable threshold: < 3 seconds for fast commands" |
| ``` |
|
|
| ### Resource Usage Testing |
|
|
| ```bash |
| # Monitor Claude Code during command execution |
| # In terminal 1: |
| claude --debug |
| |
| # In terminal 2: |
| watch -n 1 'ps aux | grep claude' |
| |
| # Execute command and observe: |
| # - Memory usage |
| # - CPU usage |
| # - Process count |
| ``` |
|
|
| ## User Experience Testing |
|
|
| ### Usability Checklist |
|
|
| - [ ] Command name is intuitive |
| - [ ] Description is clear in `/help` |
| - [ ] Arguments are well-documented |
| - [ ] Error messages are helpful |
| - [ ] Output is formatted readably |
| - [ ] Long-running commands show progress |
| - [ ] Results are actionable |
| - [ ] Edge cases have good UX |
|
|
| ### User Acceptance Testing |
|
|
| Recruit testers: |
|
|
| ```markdown |
| # Testing Guide for Beta Testers |
| |
| ## Command: /my-new-command |
| |
| ### Test Scenarios |
| |
| 1. **Basic usage:** |
| - Run: `/my-new-command` |
| - Expected: [describe] |
| - Rate clarity: 1-5 |
| |
| 2. **With arguments:** |
| - Run: `/my-new-command arg1 arg2` |
| - Expected: [describe] |
| - Rate usefulness: 1-5 |
| |
| 3. **Error case:** |
| - Run: `/my-new-command invalid-input` |
| - Expected: Helpful error message |
| - Rate error message: 1-5 |
| |
| ### Feedback Questions |
| |
| 1. Was the command easy to understand? |
| 2. Did the output meet your expectations? |
| 3. What would you change? |
| 4. Would you use this command regularly? |
| ``` |
|
|
| ## Testing Checklist |
|
|
| Before releasing a command: |
|
|
| ### Structure |
| - [ ] File in correct location |
| - [ ] Correct .md extension |
| - [ ] Valid YAML frontmatter (if present) |
| - [ ] Markdown syntax correct |
|
|
| ### Functionality |
| - [ ] Command appears in `/help` |
| - [ ] Description is clear |
| - [ ] Command executes without errors |
| - [ ] Arguments work as expected |
| - [ ] File references work |
| - [ ] Bash execution works (if used) |
|
|
| ### Edge Cases |
| - [ ] Missing arguments handled |
| - [ ] Invalid arguments detected |
| - [ ] Non-existent files handled |
| - [ ] Special characters work |
| - [ ] Long inputs handled |
|
|
| ### Integration |
| - [ ] Works with other commands |
| - [ ] Works with hooks (if applicable) |
| - [ ] Works with MCP (if applicable) |
| - [ ] State management works |
|
|
| ### Quality |
| - [ ] Performance acceptable |
| - [ ] No security issues |
| - [ ] Error messages helpful |
| - [ ] Output formatted well |
| - [ ] Documentation complete |
|
|
| ### Distribution |
| - [ ] Tested by others |
| - [ ] Feedback incorporated |
| - [ ] README updated |
| - [ ] Examples provided |
|
|
| ## Debugging Failed Tests |
|
|
| ### Common Issues and Solutions |
|
|
| **Issue: Command not appearing in /help** |
|
|
| ```bash |
| # Check file location |
| ls -la .claude/commands/my-command.md |
| |
| # Check permissions |
| chmod 644 .claude/commands/my-command.md |
| |
| # Check syntax |
| head -n 20 .claude/commands/my-command.md |
| |
| # Restart Claude Code |
| claude --debug |
| ``` |
|
|
| **Issue: Arguments not substituting** |
|
|
| ```bash |
| # Verify syntax |
| grep '\$1' .claude/commands/my-command.md |
| grep '\$ARGUMENTS' .claude/commands/my-command.md |
| |
| # Test with simple command first |
| echo "Test: \$1 and \$2" > .claude/commands/test-args.md |
| ``` |
|
|
| **Issue: Bash commands not executing** |
|
|
| ```bash |
| # Check allowed-tools |
| grep "allowed-tools" .claude/commands/my-command.md |
| |
| # Verify command syntax |
| grep '!\`' .claude/commands/my-command.md |
| |
| # Test command manually |
| date |
| echo "test" |
| ``` |
|
|
| **Issue: File references not working** |
|
|
| ```bash |
| # Check @ syntax |
| grep '@' .claude/commands/my-command.md |
| |
| # Verify file exists |
| ls -la /path/to/referenced/file |
| |
| # Check permissions |
| chmod 644 /path/to/referenced/file |
| ``` |
|
|
| ## Best Practices |
|
|
| 1. **Test early, test often**: Validate as you develop |
| 2. **Automate validation**: Use scripts for repeatable checks |
| 3. **Test edge cases**: Don't just test the happy path |
| 4. **Get feedback**: Have others test before wide release |
| 5. **Document tests**: Keep test scenarios for regression testing |
| 6. **Monitor in production**: Watch for issues after release |
| 7. **Iterate**: Improve based on real usage data |
|
|