shell-testing-framework
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseShell Testing Framework Expert
Shell测试框架专家
Comprehensive testing expertise for bash shell scripts using patterns and methodologies from the unix-goto project, emphasizing 100% test coverage, systematic test organization, and performance validation.
基于unix-goto项目的模式与方法论,提供bash Shell脚本的全面测试技能,重点强调100%测试覆盖率、系统化测试组织及性能验证。
When to Use This Skill
何时使用该技能
Use this skill when:
- Writing test suites for bash shell scripts
- Implementing 100% test coverage requirements
- Organizing tests into unit, integration, edge case, and performance categories
- Creating assertion patterns for shell script validation
- Setting up test infrastructure and helpers
- Writing performance tests for shell functions
- Generating test reports and summaries
- Debugging test failures
- Validating shell script behavior
Do NOT use this skill for:
- Testing non-shell applications (use language-specific frameworks)
- Simple ad-hoc script validation
- Production testing (use for development/CI only)
- General QA testing (this is developer-focused unit testing)
在以下场景使用该技能:
- 为bash Shell脚本编写测试套件
- 实现100%测试覆盖率要求
- 将测试划分为单元测试、集成测试、边缘测试和性能测试四类
- 为Shell脚本验证创建断言模式
- 搭建测试基础设施与辅助工具
- 为Shell函数编写性能测试
- 生成测试报告与摘要
- 调试测试失败问题
- 验证Shell脚本行为
请勿在以下场景使用:
- 测试非Shell应用(使用对应语言的专属框架)
- 简单的临时脚本验证
- 生产环境测试(仅用于开发/CI环节)
- 通用QA测试(此技能面向开发者的单元测试)
Core Testing Philosophy
核心测试理念
The 100% Coverage Rule
100%覆盖率规则
Every core feature in unix-goto has 100% test coverage. This is NON-NEGOTIABLE.
Coverage Requirements:
- Core navigation: 100%
- Cache system: 100%
- Bookmarks: 100%
- History: 100%
- Benchmarks: 100%
- New features: 100%
What This Means:
- Every function has tests
- Every code path is exercised
- Every error condition is validated
- Every edge case is covered
- Every performance target is verified
unix-goto中的每个核心功能都必须达到100%测试覆盖率,这是不可协商的要求。
覆盖率要求:
- 核心导航:100%
- 缓存系统:100%
- 书签功能:100%
- 历史记录:100%
- 基准测试:100%
- 新功能:100%
具体含义:
- 每个函数都有对应测试
- 每个代码路径都被执行
- 每个错误场景都被验证
- 每个边缘情况都被覆盖
- 每个性能指标都被验证
Test-Driven Development Approach
测试驱动开发流程
Workflow:
- Write tests FIRST (based on feature spec)
- Watch tests FAIL (red)
- Implement feature
- Watch tests PASS (green)
- Refactor if needed
- Validate all tests still pass
工作流:
- 先编写测试(基于功能规格)
- 观察测试失败(红色状态)
- 实现功能
- 观察测试通过(绿色状态)
- 按需重构代码
- 验证所有测试仍能通过
Core Knowledge
核心知识
Standard Test File Structure
标准测试文件结构
Every test file follows this exact structure:
bash
#!/bin/bash每个测试文件必须遵循以下精确结构:
bash
#!/bin/bashTest suite for [feature] functionality
Test suite for [feature] functionality
set -e # Exit on error
set -e # Exit on error
============================================
============================================
Setup
Setup
============================================
============================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/module.sh"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/module.sh"
============================================
============================================
Test Counters
Test Counters
============================================
============================================
TESTS_PASSED=0
TESTS_FAILED=0
TESTS_PASSED=0
TESTS_FAILED=0
============================================
============================================
Test Helpers
Test Helpers
============================================
============================================
pass() {
echo "✓ PASS: $1"
((TESTS_PASSED++))
}
fail() {
echo "✗ FAIL: $1"
((TESTS_FAILED++))
}
pass() {
echo "✓ PASS: $1"
((TESTS_PASSED++))
}
fail() {
echo "✗ FAIL: $1"
((TESTS_FAILED++))
}
============================================
============================================
Test Functions
Test Functions
============================================
============================================
Test 1: [Category] - [Description]
Test 1: [Category] - [Description]
test_feature_basic() {
# Arrange
local input="test"
local expected="expected_output"
# Act
local result=$(function_under_test "$input")
# Assert
if [[ "$result" == "$expected" ]]; then
pass "Basic feature test"
else
fail "Basic feature test: expected '$expected', got '$result'"
fi}
test_feature_basic() {
# Arrange
local input="test"
local expected="expected_output"
# Act
local result=$(function_under_test "$input")
# Assert
if [[ "$result" == "$expected" ]]; then
pass "Basic feature test"
else
fail "Basic feature test: expected '$expected', got '$result'"
fi}
============================================
============================================
Test Execution
Test Execution
============================================
============================================
Run all tests
Run all tests
test_feature_basic
test_feature_basic
============================================
============================================
Summary
Summary
============================================
============================================
echo ""
echo "═══════════════════════════════════════"
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
echo "═══════════════════════════════════════"
echo ""
echo "═══════════════════════════════════════"
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
echo "═══════════════════════════════════════"
Exit with proper code
Exit with proper code
[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefined[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefinedThe Arrange-Act-Assert Pattern
准备-执行-断言模式
EVERY test function MUST follow this three-phase structure:
1. Arrange - Set up test conditions
bash
undefined每个测试函数必须遵循这个三阶段结构:
1. 准备 - 设置测试条件
bash
undefinedArrange
Arrange
local input="test-value"
local expected="expected-result"
local temp_file=$(mktemp)
echo "test data" > "$temp_file"
**2. Act** - Execute the code under test
```bashlocal input="test-value"
local expected="expected-result"
local temp_file=$(mktemp)
echo "test data" > "$temp_file"
**2. 执行** - 运行待测试代码
```bashAct
Act
local result=$(function_under_test "$input")
local exit_code=$?
**3. Assert** - Verify the results
```bashlocal result=$(function_under_test "$input")
local exit_code=$?
**3. 断言** - 验证结果
```bashAssert
Assert
if [[ "$result" == "$expected" && $exit_code -eq 0 ]]; then
pass "Test description"
else
fail "Test failed: expected '$expected', got '$result'"
fi
**Complete Example:**
```bash
test_cache_lookup_single_match() {
# Arrange - Create cache with single match
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFif [[ "$result" == "$expected" && $exit_code -eq 0 ]]; then
pass "Test description"
else
fail "Test failed: expected '$expected', got '$result'"
fi
**完整示例:**
```bash
test_cache_lookup_single_match() {
# Arrange - Create cache with single match
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act - Lookup folder
local result=$(__goto_cache_lookup "unix-goto")
local exit_code=$?
# Assert - Should return exact path
local expected="/Users/manu/Git_Repos/unix-goto"
if [[ "$result" == "$expected" && $exit_code -eq 0 ]]; then
pass "Cache lookup returns single match"
else
fail "Expected '$expected' with code 0, got '$result' with code $exit_code"
fi}
undefined#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act - Lookup folder
local result=$(__goto_cache_lookup "unix-goto")
local exit_code=$?
# Assert - Should return exact path
local expected="/Users/manu/Git_Repos/unix-goto"
if [[ "$result" == "$expected" && $exit_code -eq 0 ]]; then
pass "Cache lookup returns single match"
else
fail "Expected '$expected' with code 0, got '$result' with code $exit_code"
fi}
undefinedThe Four Test Categories
四类测试分类
EVERY feature requires tests in ALL four categories:
每个功能都需要覆盖所有四类测试:
Category 1: Unit Tests
分类1:单元测试
Purpose: Test individual functions in isolation
Characteristics:
- Single function under test
- Minimal dependencies
- Fast execution (<1ms per test)
- Clear, focused assertions
Example - Cache Lookup Unit Test:
bash
test_cache_lookup_not_found() {
# Arrange
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF目的: 独立测试单个函数
特点:
- 仅测试单个函数
- 依赖项极少
- 执行速度快(每个测试<1ms)
- 断言清晰、聚焦
示例 - 缓存查找单元测试:
bash
test_cache_lookup_not_found() {
# Arrange
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "nonexistent")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Cache lookup not found returns code 1"
else
fail "Expected empty result with code 1, got '$result' with code $exit_code"
fi}
test_cache_lookup_multiple_matches() {
# Arrange
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "nonexistent")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Cache lookup not found returns code 1"
else
fail "Expected empty result with code 1, got '$result' with code $exit_code"
fi}
test_cache_lookup_multiple_matches() {
# Arrange
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
unix-goto folder index cache
unix-goto folder index cache
#---
project|/Users/manu/project1|2|1234567890
project|/Users/manu/project2|2|1234567891
EOF
# Act
local result=$(__goto_cache_lookup "project")
local exit_code=$?
# Assert - Should return all matches with code 2
local line_count=$(echo "$result" | wc -l)
if [[ $line_count -eq 2 && $exit_code -eq 2 ]]; then
pass "Cache lookup returns multiple matches with code 2"
else
fail "Expected 2 lines with code 2, got $line_count lines with code $exit_code"
fi}
**Unit Test Checklist:**
- [ ] Test with valid input
- [ ] Test with invalid input
- [ ] Test with empty input
- [ ] Test with boundary values
- [ ] Test return codes
- [ ] Test output format#---
project|/Users/manu/project1|2|1234567890
project|/Users/manu/project2|2|1234567891
EOF
# Act
local result=$(__goto_cache_lookup "project")
local exit_code=$?
# Assert - Should return all matches with code 2
local line_count=$(echo "$result" | wc -l)
if [[ $line_count -eq 2 && $exit_code -eq 2 ]]; then
pass "Cache lookup returns multiple matches with code 2"
else
fail "Expected 2 lines with code 2, got $line_count lines with code $exit_code"
fi}
**单元测试检查清单:**
- [ ] 测试有效输入
- [ ] 测试无效输入
- [ ] 测试空输入
- [ ] 测试边界值
- [ ] 测试返回码
- [ ] 测试输出格式Category 2: Integration Tests
分类2:集成测试
Purpose: Test how multiple modules work together
Characteristics:
- Multiple functions/modules interact
- Test realistic workflows
- Validate end-to-end behavior
- Moderate execution time (<100ms per test)
Example - Navigation Integration Test:
bash
test_navigation_with_cache() {
# Arrange - Setup complete navigation environment
local cache_file="$HOME/.goto_index"
local history_file="$HOME/.goto_history"
cat > "$cache_file" << EOF目的: 测试多个模块协同工作的情况
特点:
- 多个函数/模块交互
- 测试真实工作流
- 验证端到端行为
- 执行时间中等(每个测试<100ms)
示例 - 导航集成测试:
bash
test_navigation_with_cache() {
# Arrange - Setup complete navigation environment
local cache_file="$HOME/.goto_index"
local history_file="$HOME/.goto_history"
cat > "$cache_file" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act - Perform full navigation
local start_dir=$(pwd)
goto unix-goto
local nav_exit_code=$?
local end_dir=$(pwd)
# Assert - Should navigate and track history
local expected_dir="/Users/manu/Git_Repos/unix-goto"
local history_recorded=false
if grep -q "$expected_dir" "$history_file" 2>/dev/null; then
history_recorded=true
fi
if [[ "$end_dir" == "$expected_dir" && $nav_exit_code -eq 0 && $history_recorded == true ]]; then
pass "Navigation with cache and history tracking"
else
fail "Integration test failed: nav=$nav_exit_code, dir=$end_dir, history=$history_recorded"
fi
# Cleanup
cd "$start_dir"}
test_bookmark_creation_and_navigation() {
# Arrange
local bookmark_file="$HOME/.goto_bookmarks"
rm -f "$bookmark_file"
# Act - Create bookmark and navigate
bookmark add testwork /Users/manu/work
local add_code=$?
goto @testwork
local nav_code=$?
local nav_dir=$(pwd)
# Assert
local expected_dir="/Users/manu/work"
if [[ $add_code -eq 0 && $nav_code -eq 0 && "$nav_dir" == "$expected_dir" ]]; then
pass "Bookmark creation and navigation integration"
else
fail "Integration failed: add=$add_code, nav=$nav_code, dir=$nav_dir"
fi}
**Integration Test Checklist:**
- [ ] Test common user workflows
- [ ] Test module interactions
- [ ] Test data persistence
- [ ] Test state changes
- [ ] Test error propagation
- [ ] Test cleanup behavior#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act - Perform full navigation
local start_dir=$(pwd)
goto unix-goto
local nav_exit_code=$?
local end_dir=$(pwd)
# Assert - Should navigate and track history
local expected_dir="/Users/manu/Git_Repos/unix-goto"
local history_recorded=false
if grep -q "$expected_dir" "$history_file" 2>/dev/null; then
history_recorded=true
fi
if [[ "$end_dir" == "$expected_dir" && $nav_exit_code -eq 0 && $history_recorded == true ]]; then
pass "Navigation with cache and history tracking"
else
fail "Integration test failed: nav=$nav_exit_code, dir=$end_dir, history=$history_recorded"
fi
# Cleanup
cd "$start_dir"}
test_bookmark_creation_and_navigation() {
# Arrange
local bookmark_file="$HOME/.goto_bookmarks"
rm -f "$bookmark_file"
# Act - Create bookmark and navigate
bookmark add testwork /Users/manu/work
local add_code=$?
goto @testwork
local nav_code=$?
local nav_dir=$(pwd)
# Assert
local expected_dir="/Users/manu/work"
if [[ $add_code -eq 0 && $nav_code -eq 0 && "$nav_dir" == "$expected_dir" ]]; then
pass "Bookmark creation and navigation integration"
else
fail "Integration failed: add=$add_code, nav=$nav_code, dir=$nav_dir"
fi}
**集成测试检查清单:**
- [ ] 测试常见用户工作流
- [ ] 测试模块交互
- [ ] 测试数据持久化
- [ ] 测试状态变更
- [ ] 测试错误传播
- [ ] 测试清理行为Category 3: Edge Cases
分类3:边缘测试
Purpose: Test boundary conditions and unusual scenarios
Characteristics:
- Unusual but valid inputs
- Boundary conditions
- Error scenarios
- Race conditions
- Resource limits
Example - Edge Case Tests:
bash
test_empty_cache_file() {
# Arrange - Create empty cache file
local cache_file="$HOME/.goto_index"
touch "$cache_file"
# Act
local result=$(__goto_cache_lookup "anything")
local exit_code=$?
# Assert - Should handle gracefully
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Empty cache file handled gracefully"
else
fail "Empty cache should return code 1"
fi
}
test_malformed_cache_entry() {
# Arrange - Cache with malformed entry
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF目的: 测试边界条件和异常场景
特点:
- 异常但有效的输入
- 边界条件
- 错误场景
- 竞争条件
- 资源限制
示例 - 边缘测试用例:
bash
test_empty_cache_file() {
# Arrange - Create empty cache file
local cache_file="$HOME/.goto_index"
touch "$cache_file"
# Act
local result=$(__goto_cache_lookup "anything")
local exit_code=$?
# Assert - Should handle gracefully
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Empty cache file handled gracefully"
else
fail "Empty cache should return code 1"
fi
}
test_malformed_cache_entry() {
# Arrange - Cache with malformed entry
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
unix-goto|/path|missing|fields
valid-entry|/valid/path|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "valid-entry")
local exit_code=$?
# Assert - Should still find valid entry
if [[ "$result" == "/valid/path" && $exit_code -eq 0 ]]; then
pass "Malformed entry doesn't break valid lookups"
else
fail "Should handle malformed entries gracefully"
fi}
test_very_long_path() {
# Arrange - Create entry with very long path
local long_path=$(printf '/very/long/path/%.0s' {1..50})
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
#---
unix-goto|/path|missing|fields
valid-entry|/valid/path|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "valid-entry")
local exit_code=$?
# Assert - Should still find valid entry
if [[ "$result" == "/valid/path" && $exit_code -eq 0 ]]; then
pass "Malformed entry doesn't break valid lookups"
else
fail "Should handle malformed entries gracefully"
fi}
test_very_long_path() {
# Arrange - Create entry with very long path
local long_path=$(printf '/very/long/path/%.0s' {1..50})
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
unix-goto folder index cache
unix-goto folder index cache
#---
longpath|${long_path}|50|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "longpath")
local exit_code=$?
# Assert - Should handle long paths
if [[ "$result" == "$long_path" && $exit_code -eq 0 ]]; then
pass "Very long paths handled correctly"
else
fail "Long path handling failed"
fi}
test_special_characters_in_folder_name() {
# Arrange - Folder with special characters
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
#---
longpath|${long_path}|50|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "longpath")
local exit_code=$?
# Assert - Should handle long paths
if [[ "$result" == "$long_path" && $exit_code -eq 0 ]]; then
pass "Very long paths handled correctly"
else
fail "Long path handling failed"
fi}
test_special_characters_in_folder_name() {
# Arrange - Folder with special characters
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF
unix-goto folder index cache
unix-goto folder index cache
#---
my-project_v2.0|/Users/manu/my-project_v2.0|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "my-project_v2.0")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/my-project_v2.0" && $exit_code -eq 0 ]]; then
pass "Special characters in folder name"
else
fail "Special character handling failed"
fi}
test_concurrent_cache_access() {
# Arrange
local cache_file="$HOME/.goto_index"
__goto_cache_build
# Act - Simulate concurrent access
(
for i in {1..10}; do
__goto_cache_lookup "unix-goto" &
done
wait
)
local exit_code=$?
# Assert - Should handle concurrent reads
if [[ $exit_code -eq 0 ]]; then
pass "Concurrent cache access handled"
else
fail "Concurrent access failed"
fi}
**Edge Case Test Checklist:**
- [ ] Empty inputs
- [ ] Missing files
- [ ] Malformed data
- [ ] Very large inputs
- [ ] Special characters
- [ ] Concurrent access
- [ ] Resource exhaustion
- [ ] Permission errors#---
my-project_v2.0|/Users/manu/my-project_v2.0|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "my-project_v2.0")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/my-project_v2.0" && $exit_code -eq 0 ]]; then
pass "Special characters in folder name"
else
fail "Special character handling failed"
fi}
test_concurrent_cache_access() {
# Arrange
local cache_file="$HOME/.goto_index"
__goto_cache_build
# Act - Simulate concurrent access
(
for i in {1..10}; do
__goto_cache_lookup "unix-goto" &
done
wait
)
local exit_code=$?
# Assert - Should handle concurrent reads
if [[ $exit_code -eq 0 ]]; then
pass "Concurrent cache access handled"
else
fail "Concurrent access failed"
fi}
**边缘测试检查清单:**
- [ ] 空输入
- [ ] 缺失文件
- [ ] 格式错误的数据
- [ ] 超大输入
- [ ] 特殊字符
- [ ] 并发访问
- [ ] 资源耗尽
- [ ] 权限错误Category 4: Performance Tests
分类4:性能测试
Purpose: Validate performance targets are met
Characteristics:
- Measure execution time
- Compare against targets
- Use statistical analysis
- Test at scale
Example - Performance Tests:
bash
test_cache_lookup_speed() {
# Arrange - Build cache
__goto_cache_build
# Act - Measure lookup time
local start=$(date +%s%N)
__goto_cache_lookup "unix-goto"
local end=$(date +%s%N)
# Assert - Should be <100ms
local duration=$(((end - start) / 1000000))
local target=100
if [ $duration -lt $target ]; then
pass "Cache lookup speed: ${duration}ms (target: <${target}ms)"
else
fail "Cache too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_cache_build_performance() {
# Arrange - Clean cache
rm -f ~/.goto_index
# Act - Measure build time
local start=$(date +%s%N)
__goto_cache_build
local end=$(date +%s%N)
# Assert - Should be <5 seconds
local duration=$(((end - start) / 1000000))
local target=5000
if [ $duration -lt $target ]; then
pass "Cache build speed: ${duration}ms (target: <${target}ms)"
else
fail "Cache build too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_history_retrieval_speed() {
# Arrange - Create history with 100 entries
local history_file="$HOME/.goto_history"
rm -f "$history_file"
for i in {1..100}; do
echo "$(date +%s)|/path/to/dir$i" >> "$history_file"
done
# Act - Measure retrieval time
local start=$(date +%s%N)
__goto_recent_dirs 10
local end=$(date +%s%N)
# Assert - Should be <10ms
local duration=$(((end - start) / 1000000))
local target=10
if [ $duration -lt $target ]; then
pass "History retrieval: ${duration}ms (target: <${target}ms)"
else
fail "History too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_benchmark_cache_at_scale() {
# Arrange - Create large workspace
local workspace=$(mktemp -d)
for i in {1..500}; do
mkdir -p "$workspace/folder-$i"
done
# Act - Build cache and measure lookup
local old_paths="$GOTO_SEARCH_PATHS"
export GOTO_SEARCH_PATHS="$workspace"
__goto_cache_build
local start=$(date +%s%N)
__goto_cache_lookup "folder-250"
local end=$(date +%s%N)
# Assert - Even with 500 folders, should be <100ms
local duration=$(((end - start) / 1000000))
local target=100
if [ $duration -lt $target ]; then
pass "Cache at scale (500 folders): ${duration}ms"
else
fail "Cache at scale too slow: ${duration}ms"
fi
# Cleanup
export GOTO_SEARCH_PATHS="$old_paths"
rm -rf "$workspace"
}Performance Test Checklist:
- Measure critical path operations
- Compare against defined targets
- Test at realistic scale
- Test with maximum load
- Calculate statistics (min/max/mean/median)
- Verify no performance regressions
目的: 验证性能指标是否达标
特点:
- 测量执行时间
- 与目标值对比
- 使用统计分析
- 大规模测试
示例 - 性能测试用例:
bash
test_cache_lookup_speed() {
# Arrange - Build cache
__goto_cache_build
# Act - Measure lookup time
local start=$(date +%s%N)
__goto_cache_lookup "unix-goto"
local end=$(date +%s%N)
# Assert - Should be <100ms
local duration=$(((end - start) / 1000000))
local target=100
if [ $duration -lt $target ]; then
pass "Cache lookup speed: ${duration}ms (target: <${target}ms)"
else
fail "Cache too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_cache_build_performance() {
# Arrange - Clean cache
rm -f ~/.goto_index
# Act - Measure build time
local start=$(date +%s%N)
__goto_cache_build
local end=$(date +%s%N)
# Assert - Should be <5 seconds
local duration=$(((end - start) / 1000000))
local target=5000
if [ $duration -lt $target ]; then
pass "Cache build speed: ${duration}ms (target: <${target}ms)"
else
fail "Cache build too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_history_retrieval_speed() {
# Arrange - Create history with 100 entries
local history_file="$HOME/.goto_history"
rm -f "$history_file"
for i in {1..100}; do
echo "$(date +%s)|/path/to/dir$i" >> "$history_file"
done
# Act - Measure retrieval time
local start=$(date +%s%N)
__goto_recent_dirs 10
local end=$(date +%s%N)
# Assert - Should be <10ms
local duration=$(((end - start) / 1000000))
local target=10
if [ $duration -lt $target ]; then
pass "History retrieval: ${duration}ms (target: <${target}ms)"
else
fail "History too slow: ${duration}ms (target: <${target}ms)"
fi
}
test_benchmark_cache_at_scale() {
# Arrange - Create large workspace
local workspace=$(mktemp -d)
for i in {1..500}; do
mkdir -p "$workspace/folder-$i"
done
# Act - Build cache and measure lookup
local old_paths="$GOTO_SEARCH_PATHS"
export GOTO_SEARCH_PATHS="$workspace"
__goto_cache_build
local start=$(date +%s%N)
__goto_cache_lookup "folder-250"
local end=$(date +%s%N)
# Assert - Even with 500 folders, should be <100ms
local duration=$(((end - start) / 1000000))
local target=100
if [ $duration -lt $target ]; then
pass "Cache at scale (500 folders): ${duration}ms"
else
fail "Cache at scale too slow: ${duration}ms"
fi
# Cleanup
export GOTO_SEARCH_PATHS="$old_paths"
rm -rf "$workspace"
}性能测试检查清单:
- 测量关键路径操作
- 与定义的目标值对比
- 真实规模测试
- 最大负载测试
- 计算统计数据(最小值/最大值/平均值/中位数)
- 验证无性能退化
Assertion Patterns
断言模式
Basic Assertions
基础断言
String Equality:
bash
assert_equal() {
local expected="$1"
local actual="$2"
local message="${3:-String equality}"
if [[ "$actual" == "$expected" ]]; then
pass "$message"
else
fail "$message: expected '$expected', got '$actual'"
fi
}字符串相等:
bash
assert_equal() {
local expected="$1"
local actual="$2"
local message="${3:-String equality}"
if [[ "$actual" == "$expected" ]]; then
pass "$message"
else
fail "$message: expected '$expected', got '$actual'"
fi
}Usage
Usage
assert_equal "expected" "$result" "Function returns expected value"
**Exit Code Assertions:**
```bash
assert_success() {
local exit_code=$?
local message="${1:-Command should succeed}"
if [ $exit_code -eq 0 ]; then
pass "$message"
else
fail "$message: exit code $exit_code"
fi
}
assert_failure() {
local exit_code=$?
local message="${1:-Command should fail}"
if [ $exit_code -ne 0 ]; then
pass "$message"
else
fail "$message: expected non-zero exit code"
fi
}assert_equal "expected" "$result" "Function returns expected value"
**退出码断言:**
```bash
assert_success() {
local exit_code=$?
local message="${1:-Command should succeed}"
if [ $exit_code -eq 0 ]; then
pass "$message"
else
fail "$message: exit code $exit_code"
fi
}
assert_failure() {
local exit_code=$?
local message="${1:-Command should fail}"
if [ $exit_code -ne 0 ]; then
pass "$message"
else
fail "$message: expected non-zero exit code"
fi
}Usage
Usage
some_command
assert_success "Command executed successfully"
**Numeric Comparisons:**
```bash
assert_less_than() {
local actual=$1
local limit=$2
local message="${3:-Value should be less than limit}"
if [ $actual -lt $limit ]; then
pass "$message: $actual < $limit"
else
fail "$message: $actual >= $limit"
fi
}
assert_greater_than() {
local actual=$1
local limit=$2
local message="${3:-Value should be greater than limit}"
if [ $actual -gt $limit ]; then
pass "$message: $actual > $limit"
else
fail "$message: $actual <= $limit"
fi
}some_command
assert_success "Command executed successfully"
**数值比较:**
```bash
assert_less_than() {
local actual=$1
local limit=$2
local message="${3:-Value should be less than limit}"
if [ $actual -lt $limit ]; then
pass "$message: $actual < $limit"
else
fail "$message: $actual >= $limit"
fi
}
assert_greater_than() {
local actual=$1
local limit=$2
local message="${3:-Value should be greater than limit}"
if [ $actual -gt $limit ]; then
pass "$message: $actual > $limit"
else
fail "$message: $actual <= $limit"
fi
}Usage
Usage
assert_less_than $duration 100 "Cache lookup time"
undefinedassert_less_than $duration 100 "Cache lookup time"
undefinedFile System Assertions
文件系统断言
File Existence:
bash
assert_file_exists() {
local file="$1"
local message="${2:-File should exist}"
if [ -f "$file" ]; then
pass "$message: $file"
else
fail "$message: $file not found"
fi
}
assert_dir_exists() {
local dir="$1"
local message="${2:-Directory should exist}"
if [ -d "$dir" ]; then
pass "$message: $dir"
else
fail "$message: $dir not found"
fi
}文件存在性:
bash
assert_file_exists() {
local file="$1"
local message="${2:-File should exist}"
if [ -f "$file" ]; then
pass "$message: $file"
else
fail "$message: $file not found"
fi
}
assert_dir_exists() {
local dir="$1"
local message="${2:-Directory should exist}"
if [ -d "$dir" ]; then
pass "$message: $dir"
else
fail "$message: $dir not found"
fi
}Usage
Usage
assert_file_exists "$HOME/.goto_index" "Cache file created"
**File Content Assertions:**
```bash
assert_file_contains() {
local file="$1"
local pattern="$2"
local message="${3:-File should contain pattern}"
if grep -q "$pattern" "$file" 2>/dev/null; then
pass "$message"
else
fail "$message: pattern '$pattern' not found in $file"
fi
}
assert_line_count() {
local file="$1"
local expected=$2
local message="${3:-File should have expected line count}"
local actual=$(wc -l < "$file" | tr -d ' ')
if [ $actual -eq $expected ]; then
pass "$message: $actual lines"
else
fail "$message: expected $expected lines, got $actual"
fi
}assert_file_exists "$HOME/.goto_index" "Cache file created"
**文件内容断言:**
```bash
assert_file_contains() {
local file="$1"
local pattern="$2"
local message="${3:-File should contain pattern}"
if grep -q "$pattern" "$file" 2>/dev/null; then
pass "$message"
else
fail "$message: pattern '$pattern' not found in $file"
fi
}
assert_line_count() {
local file="$1"
local expected=$2
local message="${3:-File should have expected line count}"
local actual=$(wc -l < "$file" | tr -d ' ')
if [ $actual -eq $expected ]; then
pass "$message: $actual lines"
else
fail "$message: expected $expected lines, got $actual"
fi
}Usage
Usage
assert_file_contains "$HOME/.goto_bookmarks" "work|/path/to/work"
assert_line_count "$HOME/.goto_history" 10
undefinedassert_file_contains "$HOME/.goto_bookmarks" "work|/path/to/work"
assert_line_count "$HOME/.goto_history" 10
undefinedOutput Assertions
输出断言
Contains Pattern:
bash
assert_output_contains() {
local output="$1"
local pattern="$2"
local message="${3:-Output should contain pattern}"
if [[ "$output" =~ $pattern ]]; then
pass "$message"
else
fail "$message: pattern '$pattern' not found in output"
fi
}包含指定模式:
bash
assert_output_contains() {
local output="$1"
local pattern="$2"
local message="${3:-Output should contain pattern}"
if [[ "$output" =~ $pattern ]]; then
pass "$message"
else
fail "$message: pattern '$pattern' not found in output"
fi
}Usage
Usage
output=$(goto recent)
assert_output_contains "$output" "/Users/manu/work" "Recent shows work directory"
**Empty Output:**
```bash
assert_output_empty() {
local output="$1"
local message="${2:-Output should be empty}"
if [[ -z "$output" ]]; then
pass "$message"
else
fail "$message: got '$output'"
fi
}output=$(goto recent)
assert_output_contains "$output" "/Users/manu/work" "Recent shows work directory"
**空输出:**
```bash
assert_output_empty() {
local output="$1"
local message="${2:-Output should be empty}"
if [[ -z "$output" ]]; then
pass "$message"
else
fail "$message: got '$output'"
fi
}Usage
Usage
output=$(goto nonexistent 2>&1)
assert_output_empty "$output"
undefinedoutput=$(goto nonexistent 2>&1)
assert_output_empty "$output"
undefinedTest Helper Functions
测试辅助函数
Create a reusable test helpers library:
bash
#!/bin/bash创建可复用的测试辅助函数库:
bash
#!/bin/bashtest-helpers.sh - Reusable test utilities
test-helpers.sh - Reusable test utilities
============================================
============================================
Setup/Teardown
Setup/Teardown
============================================
============================================
setup_test_env() {
# Create temp directory for test
TEST_TEMP_DIR=$(mktemp -d)
# Backup real files
[ -f "$HOME/.goto_index" ] && cp "$HOME/.goto_index" "$TEST_TEMP_DIR/goto_index.bak"
[ -f "$HOME/.goto_bookmarks" ] && cp "$HOME/.goto_bookmarks" "$TEST_TEMP_DIR/goto_bookmarks.bak"
[ -f "$HOME/.goto_history" ] && cp "$HOME/.goto_history" "$TEST_TEMP_DIR/goto_history.bak"}
teardown_test_env() {
# Restore backups
[ -f "$TEST_TEMP_DIR/goto_index.bak" ] && mv "$TEST_TEMP_DIR/goto_index.bak" "$HOME/.goto_index"
[ -f "$TEST_TEMP_DIR/goto_bookmarks.bak" ] && mv "$TEST_TEMP_DIR/goto_bookmarks.bak" "$HOME/.goto_bookmarks"
[ -f "$TEST_TEMP_DIR/goto_history.bak" ] && mv "$TEST_TEMP_DIR/goto_history.bak" "$HOME/.goto_history"
# Remove temp directory
rm -rf "$TEST_TEMP_DIR"}
setup_test_env() {
# Create temp directory for test
TEST_TEMP_DIR=$(mktemp -d)
# Backup real files
[ -f "$HOME/.goto_index" ] && cp "$HOME/.goto_index" "$TEST_TEMP_DIR/goto_index.bak"
[ -f "$HOME/.goto_bookmarks" ] && cp "$HOME/.goto_bookmarks" "$TEST_TEMP_DIR/goto_bookmarks.bak"
[ -f "$HOME/.goto_history" ] && cp "$HOME/.goto_history" "$TEST_TEMP_DIR/goto_history.bak"}
teardown_test_env() {
# Restore backups
[ -f "$TEST_TEMP_DIR/goto_index.bak" ] && mv "$TEST_TEMP_DIR/goto_index.bak" "$HOME/.goto_index"
[ -f "$TEST_TEMP_DIR/goto_bookmarks.bak" ] && mv "$TEST_TEMP_DIR/goto_bookmarks.bak" "$HOME/.goto_bookmarks"
[ -f "$TEST_TEMP_DIR/goto_history.bak" ] && mv "$TEST_TEMP_DIR/goto_history.bak" "$HOME/.goto_history"
# Remove temp directory
rm -rf "$TEST_TEMP_DIR"}
============================================
============================================
Test Data Creation
Test Data Creation
============================================
============================================
create_test_cache() {
local entries="${1:-10}"
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFcreate_test_cache() {
local entries="${1:-10}"
local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOFunix-goto folder index cache
unix-goto folder index cache
Version: 1.0
Version: 1.0
Built: $(date +%s)
Built: $(date +%s)
Depth: 3
Depth: 3
Format: folder_name|full_path|depth|last_modified
Format: folder_name|full_path|depth|last_modified
#---
EOF
for i in $(seq 1 $entries); do
echo "folder-$i|/path/to/folder-$i|2|$(date +%s)" >> "$cache_file"
done}
create_test_bookmarks() {
local count="${1:-5}"
local bookmark_file="$HOME/.goto_bookmarks"
rm -f "$bookmark_file"
for i in $(seq 1 $count); do
echo "bookmark$i|/path/to/bookmark$i|$(date +%s)" >> "$bookmark_file"
done}
create_test_history() {
local count="${1:-20}"
local history_file="$HOME/.goto_history"
rm -f "$history_file"
for i in $(seq 1 $count); do
echo "$(date +%s)|/path/to/dir$i" >> "$history_file"
done}
#---
EOF
for i in $(seq 1 $entries); do
echo "folder-$i|/path/to/folder-$i|2|$(date +%s)" >> "$cache_file"
done}
create_test_bookmarks() {
local count="${1:-5}"
local bookmark_file="$HOME/.goto_bookmarks"
rm -f "$bookmark_file"
for i in $(seq 1 $count); do
echo "bookmark$i|/path/to/bookmark$i|$(date +%s)" >> "$bookmark_file"
done}
create_test_history() {
local count="${1:-20}"
local history_file="$HOME/.goto_history"
rm -f "$history_file"
for i in $(seq 1 $count); do
echo "$(date +%s)|/path/to/dir$i" >> "$history_file"
done}
============================================
============================================
Timing Utilities
Timing Utilities
============================================
============================================
time_function_ms() {
local func="$1"
shift
local args="$@"
local start=$(date +%s%N)
$func $args
local end=$(date +%s%N)
echo $(((end - start) / 1000000))}
time_function_ms() {
local func="$1"
shift
local args="$@"
local start=$(date +%s%N)
$func $args
local end=$(date +%s%N)
echo $(((end - start) / 1000000))}
============================================
============================================
Assertion Helpers
Assertion Helpers
============================================
============================================
assert_function_exists() {
local func="$1"
if declare -f "$func" > /dev/null; then
pass "Function $func exists"
else
fail "Function $func not found"
fi}
assert_variable_set() {
local var="$1"
if [ -n "${!var}" ]; then
pass "Variable $var is set"
else
fail "Variable $var not set"
fi}
undefinedassert_function_exists() {
local func="$1"
if declare -f "$func" > /dev/null; then
pass "Function $func exists"
else
fail "Function $func not found"
fi}
assert_variable_set() {
local var="$1"
if [ -n "${!var}" ]; then
pass "Variable $var is set"
else
fail "Variable $var not set"
fi}
undefinedExamples
示例
Example 1: Complete Cache Test Suite
示例1:完整缓存测试套件
bash
#!/bin/bashbash
#!/bin/bashtest-cache.sh - Comprehensive cache system test suite
test-cache.sh - Comprehensive cache system test suite
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/cache-index.sh"
source "$SCRIPT_DIR/test-helpers.sh"
TESTS_PASSED=0
TESTS_FAILED=0
pass() { echo "✓ PASS: $1"; ((TESTS_PASSED++)); }
fail() { echo "✗ FAIL: $1"; ((TESTS_FAILED++)); }
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/cache-index.sh"
source "$SCRIPT_DIR/test-helpers.sh"
TESTS_PASSED=0
TESTS_FAILED=0
pass() { echo "✓ PASS: $1"; ((TESTS_PASSED++)); }
fail() { echo "✗ FAIL: $1"; ((TESTS_FAILED++)); }
============================================
============================================
Unit Tests
Unit Tests
============================================
============================================
echo "Unit Tests"
echo "─────────────────────────────────────────"
test_cache_lookup_single_match() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOFecho "Unit Tests"
echo "─────────────────────────────────────────"
test_cache_lookup_single_match() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "unix-goto")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/Git_Repos/unix-goto" && $exit_code -eq 0 ]]; then
pass "Unit: Single match lookup"
else
fail "Unit: Single match lookup - got '$result' code $exit_code"
fi
teardown_test_env}
test_cache_lookup_not_found() {
setup_test_env
# Arrange
create_test_cache 5
# Act
local result=$(__goto_cache_lookup "nonexistent")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Unit: Not found returns code 1"
else
fail "Unit: Not found - got '$result' code $exit_code"
fi
teardown_test_env}
test_cache_lookup_multiple_matches() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOF#---
unix-goto|/Users/manu/Git_Repos/unix-goto|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "unix-goto")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/Git_Repos/unix-goto" && $exit_code -eq 0 ]]; then
pass "Unit: Single match lookup"
else
fail "Unit: Single match lookup - got '$result' code $exit_code"
fi
teardown_test_env}
test_cache_lookup_not_found() {
setup_test_env
# Arrange
create_test_cache 5
# Act
local result=$(__goto_cache_lookup "nonexistent")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Unit: Not found returns code 1"
else
fail "Unit: Not found - got '$result' code $exit_code"
fi
teardown_test_env}
test_cache_lookup_multiple_matches() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
project|/Users/manu/project1|2|1234567890
project|/Users/manu/project2|2|1234567891
EOF
# Act
local result=$(__goto_cache_lookup "project")
local exit_code=$?
local line_count=$(echo "$result" | wc -l | tr -d ' ')
# Assert
if [[ $line_count -eq 2 && $exit_code -eq 2 ]]; then
pass "Unit: Multiple matches returns code 2"
else
fail "Unit: Multiple matches - got $line_count lines code $exit_code"
fi
teardown_test_env}
#---
project|/Users/manu/project1|2|1234567890
project|/Users/manu/project2|2|1234567891
EOF
# Act
local result=$(__goto_cache_lookup "project")
local exit_code=$?
local line_count=$(echo "$result" | wc -l | tr -d ' ')
# Assert
if [[ $line_count -eq 2 && $exit_code -eq 2 ]]; then
pass "Unit: Multiple matches returns code 2"
else
fail "Unit: Multiple matches - got $line_count lines code $exit_code"
fi
teardown_test_env}
============================================
============================================
Integration Tests
Integration Tests
============================================
============================================
echo ""
echo "Integration Tests"
echo "─────────────────────────────────────────"
test_cache_build_and_lookup() {
setup_test_env
# Arrange
rm -f "$HOME/.goto_index"
# Act
__goto_cache_build
local build_code=$?
local result=$(__goto_cache_lookup "unix-goto")
local lookup_code=$?
# Assert
if [[ $build_code -eq 0 && $lookup_code -eq 0 && -n "$result" ]]; then
pass "Integration: Build and lookup"
else
fail "Integration: Build ($build_code) and lookup ($lookup_code) failed"
fi
teardown_test_env}
echo ""
echo "Integration Tests"
echo "─────────────────────────────────────────"
test_cache_build_and_lookup() {
setup_test_env
# Arrange
rm -f "$HOME/.goto_index"
# Act
__goto_cache_build
local build_code=$?
local result=$(__goto_cache_lookup "unix-goto")
local lookup_code=$?
# Assert
if [[ $build_code -eq 0 && $lookup_code -eq 0 && -n "$result" ]]; then
pass "Integration: Build and lookup"
else
fail "Integration: Build ($build_code) and lookup ($lookup_code) failed"
fi
teardown_test_env}
============================================
============================================
Edge Cases
Edge Cases
============================================
============================================
echo ""
echo "Edge Case Tests"
echo "─────────────────────────────────────────"
test_empty_cache_file() {
setup_test_env
# Arrange
touch "$HOME/.goto_index"
# Act
local result=$(__goto_cache_lookup "anything")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Edge: Empty cache handled"
else
fail "Edge: Empty cache should return code 1"
fi
teardown_test_env}
test_special_characters() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOFecho ""
echo "Edge Case Tests"
echo "─────────────────────────────────────────"
test_empty_cache_file() {
setup_test_env
# Arrange
touch "$HOME/.goto_index"
# Act
local result=$(__goto_cache_lookup "anything")
local exit_code=$?
# Assert
if [[ -z "$result" && $exit_code -eq 1 ]]; then
pass "Edge: Empty cache handled"
else
fail "Edge: Empty cache should return code 1"
fi
teardown_test_env}
test_special_characters() {
setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOFunix-goto folder index cache
unix-goto folder index cache
#---
my-project_v2.0|/Users/manu/my-project_v2.0|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "my-project_v2.0")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/my-project_v2.0" && $exit_code -eq 0 ]]; then
pass "Edge: Special characters in name"
else
fail "Edge: Special characters failed"
fi
teardown_test_env}
#---
my-project_v2.0|/Users/manu/my-project_v2.0|2|1234567890
EOF
# Act
local result=$(__goto_cache_lookup "my-project_v2.0")
local exit_code=$?
# Assert
if [[ "$result" == "/Users/manu/my-project_v2.0" && $exit_code -eq 0 ]]; then
pass "Edge: Special characters in name"
else
fail "Edge: Special characters failed"
fi
teardown_test_env}
============================================
============================================
Performance Tests
Performance Tests
============================================
============================================
echo ""
echo "Performance Tests"
echo "─────────────────────────────────────────"
test_cache_lookup_speed() {
setup_test_env
# Arrange
create_test_cache 100
# Act
local duration=$(time_function_ms __goto_cache_lookup "folder-50")
# Assert - Should be <100ms
if [ $duration -lt 100 ]; then
pass "Performance: Cache lookup ${duration}ms (<100ms target)"
else
fail "Performance: Cache too slow ${duration}ms"
fi
teardown_test_env}
test_cache_build_speed() {
setup_test_env
# Arrange
rm -f "$HOME/.goto_index"
# Act
local duration=$(time_function_ms __goto_cache_build)
# Assert - Should be <5000ms (5 seconds)
if [ $duration -lt 5000 ]; then
pass "Performance: Cache build ${duration}ms (<5000ms target)"
else
fail "Performance: Cache build too slow ${duration}ms"
fi
teardown_test_env}
echo ""
echo "Performance Tests"
echo "─────────────────────────────────────────"
test_cache_lookup_speed() {
setup_test_env
# Arrange
create_test_cache 100
# Act
local duration=$(time_function_ms __goto_cache_lookup "folder-50")
# Assert - Should be <100ms
if [ $duration -lt 100 ]; then
pass "Performance: Cache lookup ${duration}ms (<100ms target)"
else
fail "Performance: Cache too slow ${duration}ms"
fi
teardown_test_env}
test_cache_build_speed() {
setup_test_env
# Arrange
rm -f "$HOME/.goto_index"
# Act
local duration=$(time_function_ms __goto_cache_build)
# Assert - Should be <5000ms (5 seconds)
if [ $duration -lt 5000 ]; then
pass "Performance: Cache build ${duration}ms (<5000ms target)"
else
fail "Performance: Cache build too slow ${duration}ms"
fi
teardown_test_env}
============================================
============================================
Run All Tests
Run All Tests
============================================
============================================
test_cache_lookup_single_match
test_cache_lookup_not_found
test_cache_lookup_multiple_matches
test_cache_build_and_lookup
test_empty_cache_file
test_special_characters
test_cache_lookup_speed
test_cache_build_speed
test_cache_lookup_single_match
test_cache_lookup_not_found
test_cache_lookup_multiple_matches
test_cache_build_and_lookup
test_empty_cache_file
test_special_characters
test_cache_lookup_speed
test_cache_build_speed
============================================
============================================
Summary
Summary
============================================
============================================
echo ""
echo "═══════════════════════════════════════"
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
echo "Coverage: 100% (all code paths tested)"
echo "═══════════════════════════════════════"
[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefinedecho ""
echo "═══════════════════════════════════════"
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
echo "Coverage: 100% (all code paths tested)"
echo "═══════════════════════════════════════"
[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefinedExample 2: Benchmark Test Suite
示例2:基准测试套件
bash
#!/bin/bashbash
#!/bin/bashtest-benchmark.sh - Test suite for benchmark functionality
test-benchmark.sh - Test suite for benchmark functionality
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/benchmarks/bench-helpers.sh"
TESTS_PASSED=0
TESTS_FAILED=0
pass() { echo "✓ PASS: $1"; ((TESTS_PASSED++)); }
fail() { echo "✗ FAIL: $1"; ((TESTS_FAILED++)); }
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/benchmarks/bench-helpers.sh"
TESTS_PASSED=0
TESTS_FAILED=0
pass() { echo "✓ PASS: $1"; ((TESTS_PASSED++)); }
fail() { echo "✗ FAIL: $1"; ((TESTS_FAILED++)); }
Unit Tests
Unit Tests
test_bench_time_ms() {
# Arrange
local cmd="sleep 0.1"
# Act
local duration=$(bench_time_ms $cmd)
# Assert - Should be ~100ms
if [ $duration -ge 90 ] && [ $duration -le 150 ]; then
pass "bench_time_ms measures correctly: ${duration}ms"
else
fail "bench_time_ms inaccurate: ${duration}ms (expected ~100ms)"
fi}
test_bench_calculate_stats() {
# Arrange
local values=(10 20 30 40 50)
# Act
local stats=$(bench_calculate_stats "${values[@]}")
IFS=',' read -r min max mean median stddev <<< "$stats"
# Assert
if [[ $min -eq 10 && $max -eq 50 && $mean -eq 30 ]]; then
pass "bench_calculate_stats computes correctly"
else
fail "Stats calculation failed: min=$min max=$max mean=$mean"
fi}
test_bench_create_workspace() {
# Arrange/Act
local workspace=$(bench_create_workspace "small")
# Assert
if [ -d "$workspace" ] && [ $(ls -1 "$workspace" | wc -l) -eq 10 ]; then
pass "Workspace creation (small: 10 folders)"
bench_cleanup_workspace "$workspace"
else
fail "Workspace creation failed"
fi}
test_bench_time_ms() {
# Arrange
local cmd="sleep 0.1"
# Act
local duration=$(bench_time_ms $cmd)
# Assert - Should be ~100ms
if [ $duration -ge 90 ] && [ $duration -le 150 ]; then
pass "bench_time_ms measures correctly: ${duration}ms"
else
fail "bench_time_ms inaccurate: ${duration}ms (expected ~100ms)"
fi}
test_bench_calculate_stats() {
# Arrange
local values=(10 20 30 40 50)
# Act
local stats=$(bench_calculate_stats "${values[@]}")
IFS=',' read -r min max mean median stddev <<< "$stats"
# Assert
if [[ $min -eq 10 && $max -eq 50 && $mean -eq 30 ]]; then
pass "bench_calculate_stats computes correctly"
else
fail "Stats calculation failed: min=$min max=$max mean=$mean"
fi}
test_bench_create_workspace() {
# Arrange/Act
local workspace=$(bench_create_workspace "small")
# Assert
if [ -d "$workspace" ] && [ $(ls -1 "$workspace" | wc -l) -eq 10 ]; then
pass "Workspace creation (small: 10 folders)"
bench_cleanup_workspace "$workspace"
else
fail "Workspace creation failed"
fi}
Run tests
Run tests
test_bench_time_ms
test_bench_calculate_stats
test_bench_create_workspace
echo ""
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefinedtest_bench_time_ms
test_bench_calculate_stats
test_bench_create_workspace
echo ""
echo "Tests passed: $TESTS_PASSED"
echo "Tests failed: $TESTS_FAILED"
[ $TESTS_FAILED -eq 0 ] && exit 0 || exit 1
undefinedBest Practices
最佳实践
Test Organization
测试组织
File Naming Convention:
test-cache.sh # Test cache system
test-bookmark.sh # Test bookmarks
test-navigation.sh # Test navigation
test-benchmark.sh # Test benchmarksTest Function Naming:
test_[category]_[feature]_[scenario]
Examples:
test_unit_cache_lookup_single_match
test_integration_navigation_with_cache
test_edge_empty_input
test_performance_cache_speed文件命名规范:
test-cache.sh # 测试缓存系统
test-bookmark.sh # 测试书签功能
test-navigation.sh # 测试导航功能
test-benchmark.sh # 测试基准功能测试函数命名:
test_[category]_[feature]_[scenario]
示例:
test_unit_cache_lookup_single_match
test_integration_navigation_with_cache
test_edge_empty_input
test_performance_cache_speedTest Independence
测试独立性
Each test must be completely independent:
bash
undefined每个测试必须完全独立:
bash
undefinedGood - Independent test
良好实践 - 独立测试
test_feature() {
# Setup own environment
local temp=$(mktemp)
# Test
result=$(function_under_test)
# Cleanup own resources
rm -f "$temp"
# Assert
[[ "$result" == "expected" ]] && pass "Test" || fail "Test"}
test_feature() {
# 搭建自身测试环境
local temp=$(mktemp)
# 执行测试
result=$(function_under_test)
# 清理自身资源
rm -f "$temp"
# 断言
[[ "$result" == "expected" ]] && pass "Test" || fail "Test"}
Bad - Depends on previous test state
不良实践 - 依赖之前的测试状态
test_feature_bad() {
# Assumes something from previous test
result=$(function_under_test) # May fail if run alone
}
undefinedtest_feature_bad() {
# 假设之前的测试已设置好环境
result=$(function_under_test) # 单独运行时可能失败
}
undefinedMeaningful Failure Messages
有意义的失败信息
bash
undefinedbash
undefinedGood - Detailed failure message
良好实践 - 详细的失败信息
if [[ "$result" != "$expected" ]]; then
fail "Cache lookup failed: expected '$expected', got '$result', exit code: $exit_code"
fi
if [[ "$result" != "$expected" ]]; then
fail "Cache lookup failed: expected '$expected', got '$result', exit code: $exit_code"
fi
Bad - Vague failure message
不良实践 - 模糊的失败信息
if [[ "$result" != "$expected" ]]; then
fail "Test failed"
fi
undefinedif [[ "$result" != "$expected" ]]; then
fail "Test failed"
fi
undefinedTest Execution Speed
测试执行速度
Keep tests FAST:
- Unit tests: <1ms each
- Integration tests: <100ms each
- Edge cases: <10ms each
- Performance tests: As needed for measurement
Total test suite should run in <5 seconds.
保持测试快速:
- 单元测试:每个<1ms
- 集成测试:每个<100ms
- 边缘测试:每个<10ms
- 性能测试:按需测量
整个测试套件的运行时间应<5秒。
Quick Reference
快速参考
Test Template Checklist
测试模板检查清单
- Shebang and set -e
- Source required modules
- Initialize test counters
- Define pass/fail helpers
- Organize tests by category
- Use arrange-act-assert pattern
- Print summary with exit code
- Shebang和set -e设置
- 引入所需模块
- 初始化测试计数器
- 定义pass/fail辅助函数
- 按分类组织测试
- 使用准备-执行-断言模式
- 打印摘要并返回正确退出码
Coverage Checklist
覆盖率检查清单
- All public functions tested
- All code paths exercised
- All return codes validated
- All error conditions tested
- All edge cases covered
- Performance targets verified
- 所有公共函数已测试
- 所有代码路径已执行
- 所有返回码已验证
- 所有错误场景已测试
- 所有边缘情况已覆盖
- 性能指标已验证
Essential Test Commands
常用测试命令
bash
undefinedbash
undefinedRun single test suite
运行单个测试套件
bash test-cache.sh
bash test-cache.sh
Run all tests
运行所有测试
bash test-cache.sh && bash test-bookmark.sh && bash test-navigation.sh
bash test-cache.sh && bash test-bookmark.sh && bash test-navigation.sh
Run with verbose output
启用详细输出运行
set -x; bash test-cache.sh; set +x
set -x; bash test-cache.sh; set +x
Run specific test function
运行指定测试函数
bash -c 'source test-cache.sh; test_cache_lookup_single_match'
---
**Skill Version:** 1.0
**Last Updated:** October 2025
**Maintained By:** Manu Tej + Claude Code
**Source:** unix-goto testing patterns and methodologiesbash -c 'source test-cache.sh; test_cache_lookup_single_match'
---
**技能版本:** 1.0
**最后更新:** 2025年10月
**维护者:** Manu Tej + Claude Code
**来源:** unix-goto测试模式与方法论