mcpField
B
bats-testing-patterns

bats-testing-patterns

github.com/wshobson测试与质量41.9k11.1k 次安装2 周前更新
代码仓库

关于 bats-testing-patterns

面向编写 Shell 脚本测试的开发者,提供 Bats 测试模式,覆盖测试夹具、错误处理、兼容性及自动化集成。

主要功能

  1. 01Shell 脚本单元测试模式
  2. 02测试夹具与辅助函数
  3. 03错误条件和边界情况测试
  4. 04Shell 方言兼容性测试
  5. 05CI/CD 与 Makefile 集成

适用场景

  1. 01为 Shell 脚本编写单元测试
  2. 02验证错误处理和外部依赖
  3. 03在 CI/CD 中运行脚本测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshobson/agents --skill bats-testing-patterns

安装后在 Claude Code 中输入/bats-testing-patterns确认已加载。

附带文件references/details.md

详细介绍

Bats 测试模式

使用 Bats(Bash Automated Testing System)为 shell 脚本编写全面单元测试的综合指南,包括测试模式、fixture 以及生产级 shell 测试的最佳实践。

何时使用此技能

  • 为 shell 脚本编写单元测试
  • 为脚本实施测试驱动开发(TDD)
  • 在 CI/CD 流水线中设置自动化测试
  • 测试边界情况和错误条件
  • 验证不同 shell 环境中的行为
  • 为脚本构建可维护的测试套件
  • 为复杂测试场景创建 fixture
  • 测试多种 shell 方言(bash、sh、dash)

详细模式与完整示例

详细的模式文档位于 references/details.md。当上面的导航层级不够用时,请阅读该文件。

测试错误条件

#!/usr/bin/env bats

@test "Function fails with missing file" {
    run my_function "/nonexistent/file.txt"
    [ "$status" -ne 0 ]
    [[ "$output" == *"not found"* ]]
}

@test "Function fails with invalid input" {
    run my_function ""
    [ "$status" -ne 0 ]
}

@test "Function fails with permission denied" {
    touch "$TMPDIR/readonly.txt"
    chmod 000 "$TMPDIR/readonly.txt"
    run my_function "$TMPDIR/readonly.txt"
    [ "$status" -ne 0 ]
    chmod 644 "$TMPDIR/readonly.txt"  # Cleanup
}

@test "Function provides helpful error message" {
    run my_function --invalid-option
    [ "$status" -ne 0 ]
    [[ "$output" == *"Usage:"* ]]
}
使用依赖项进行测试
#!/usr/bin/env bats

setup() {
    # Check for required tools
    if ! command -v jq &>/dev/null; then
        skip "jq is not installed"
    fi

    export SCRIPT="${BATS_TEST_DIRNAME}/../bin/script.sh"
}

@test "JSON parsing works" {
    skip_if ! command -v jq &>/dev/null
    run my_json_parser '{"key": "value"}'
    [ "$status" -eq 0 ]
}
测试 Shell 兼容性
#!/usr/bin/env bats

@test "Script works in bash" {
    bash "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
}

@test "Script works in sh (POSIX)" {
    sh "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
}

@test "Script works in dash" {
    if command -v dash &>/dev/null; then
        dash "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
    else
        skip "dash not installed"
    fi
}
并行执行
#!/usr/bin/env bats

@test "Multiple independent operations" {
    run bash -c 'for i in {1..10}; do
        my_operation "$i" &
    done
    wait'
    [ "$status" -eq 0 ]
}

@test "Concurrent file operations" {
    for i in {1..5}; do
        my_function "$TMPDIR/file$i" &
    done
    wait
    [ -f "$TMPDIR/file1" ]
    [ -f "$TMPDIR/file5" ]
}

测试助手模式

test_helper.sh
#!/usr/bin/env bash

# Source script under test
export SCRIPT_DIR="${BATS_TEST_DIRNAME%/*}/bin"

# Common test utilities
assert_file_exists() {
    if [ ! -f "$1" ]; then
        echo "Expected file to exist: $1"
        return 1
    fi
}

assert_file_equals() {
    local file="$1"
    local expected="$2"

    if [ ! -f "$file" ]; then
        echo "File does not exist: $file"
        return 1
    fi

    local actual=$(cat "$file")
    if [ "$actual" != "$expected" ]; then
        echo "File contents do not match"
        echo "Expected: $expected"
        echo "Actual: $actual"
        return 1
    fi
}

# Create temporary test directory
setup_test_dir() {
    export TEST_DIR=$(mktemp -d)
}

cleanup_test_dir() {
    rm -rf "$TEST_DIR"
}

与 CI/CD 集成

GitHub Actions 工作流
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Install Bats
        run: |
          npm install --global bats

      - name: Run Tests
        run: |
          bats tests/*.bats

      - name: Run Tests with Tap Reporter
        run: |
          bats tests/*.bats --tap | tee test_output.tap
Makefile 集成
.PHONY: test test-verbose test-tap

test:
	bats tests/*.bats

test-verbose:
	bats tests/*.bats --verbose

test-tap:
	bats tests/*.bats --tap

test-parallel:
	bats tests/*.bats --parallel 4

coverage: test
	# Optional: Generate coverage reports

最佳实践

  1. 每个测试只测试一件事 - 单一职责原则
  2. 使用描述性的测试名称 - 清楚说明正在测试什么
  3. 测试后进行清理 - 始终在 teardown 中移除临时文件
  4. 同时测试成功和失败路径 - 不要只测试顺利路径
  5. 模拟外部依赖 - 隔离被测单元
  6. 对复杂数据使用 fixture - 让测试更具可读性
  7. 在 CI/CD 中运行测试 - 尽早发现回归
  8. 跨 shell 方言测试 - 确保可移植性
  9. 保持测试快速 - 尽可能并行运行
  10. 记录复杂的测试设置 - 解释不寻常的模式

评论

支持 Markdown