shell-testing-framework

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shell 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:
  1. Write tests FIRST (based on feature spec)
  2. Watch tests FAIL (red)
  3. Implement feature
  4. Watch tests PASS (green)
  5. Refactor if needed
  6. Validate all tests still pass
工作流:
  1. 先编写测试(基于功能规格)
  2. 观察测试失败(红色状态)
  3. 实现功能
  4. 观察测试通过(绿色状态)
  5. 按需重构代码
  6. 验证所有测试仍能通过

Core Knowledge

核心知识

Standard Test File Structure

标准测试文件结构

Every test file follows this exact structure:
bash
#!/bin/bash
每个测试文件必须遵循以下精确结构:
bash
#!/bin/bash

Test 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
undefined

The Arrange-Act-Assert Pattern

准备-执行-断言模式

EVERY test function MUST follow this three-phase structure:
1. Arrange - Set up test conditions
bash
undefined
每个测试函数必须遵循这个三阶段结构:
1. 准备 - 设置测试条件
bash
undefined

Arrange

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
```bash
local input="test-value" local expected="expected-result" local temp_file=$(mktemp) echo "test data" > "$temp_file"

**2. 执行** - 运行待测试代码
```bash

Act

Act

local result=$(function_under_test "$input") local exit_code=$?

**3. Assert** - Verify the results
```bash
local result=$(function_under_test "$input") local exit_code=$?

**3. 断言** - 验证结果
```bash

Assert

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" << EOF
if [[ "$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" << EOF

unix-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
}
undefined

The 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" << EOF

unix-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" << EOF

unix-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" << EOF

unix-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"
undefined
assert_less_than $duration 100 "Cache lookup time"
undefined

File 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
undefined
assert_file_contains "$HOME/.goto_bookmarks" "work|/path/to/work" assert_line_count "$HOME/.goto_history" 10
undefined

Output 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"
undefined
output=$(goto nonexistent 2>&1) assert_output_empty "$output"
undefined

Test Helper Functions

测试辅助函数

Create a reusable test helpers library:
bash
#!/bin/bash
创建可复用的测试辅助函数库:
bash
#!/bin/bash

test-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" << EOF
create_test_cache() { local entries="${1:-10}" local cache_file="$HOME/.goto_index"
cat > "$cache_file" << EOF

unix-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
}
undefined
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
}
undefined

Examples

示例

Example 1: Complete Cache Test Suite

示例1:完整缓存测试套件

bash
#!/bin/bash
bash
#!/bin/bash

test-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" << EOF
echo "Unit Tests" echo "─────────────────────────────────────────"
test_cache_lookup_single_match() { setup_test_env
# Arrange
cat > "$HOME/.goto_index" << EOF

unix-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" << 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=$?
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" << EOF
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" << 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 "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
undefined
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
undefined

Example 2: Benchmark Test Suite

示例2:基准测试套件

bash
#!/bin/bash
bash
#!/bin/bash

test-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
undefined
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
undefined

Best 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 benchmarks
Test 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_speed

Test Independence

测试独立性

Each test must be completely independent:
bash
undefined
每个测试必须完全独立:
bash
undefined

Good - 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 }
undefined
test_feature_bad() { # 假设之前的测试已设置好环境 result=$(function_under_test) # 单独运行时可能失败 }
undefined

Meaningful Failure Messages

有意义的失败信息

bash
undefined
bash
undefined

Good - 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
undefined
if [[ "$result" != "$expected" ]]; then fail "Test failed" fi
undefined

Test 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
undefined
bash
undefined

Run 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 methodologies
bash -c 'source test-cache.sh; test_cache_lookup_single_match'

---

**技能版本:** 1.0
**最后更新:** 2025年10月
**维护者:** Manu Tej + Claude Code
**来源:** unix-goto测试模式与方法论