关于 bats-testing-patterns
面向编写 Shell 脚本测试的开发者,提供 Bats 测试模式,覆盖测试夹具、错误处理、兼容性及自动化集成。
主要功能
- 01Shell 脚本单元测试模式
- 02测试夹具与辅助函数
- 03错误条件和边界情况测试
- 04Shell 方言兼容性测试
- 05CI/CD 与 Makefile 集成
适用场景
- 01为 Shell 脚本编写单元测试
- 02验证错误处理和外部依赖
- 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
最佳实践
- 每个测试只测试一件事 - 单一职责原则
- 使用描述性的测试名称 - 清楚说明正在测试什么
- 测试后进行清理 - 始终在 teardown 中移除临时文件
- 同时测试成功和失败路径 - 不要只测试顺利路径
- 模拟外部依赖 - 隔离被测单元
- 对复杂数据使用 fixture - 让测试更具可读性
- 在 CI/CD 中运行测试 - 尽早发现回归
- 跨 shell 方言测试 - 确保可移植性
- 保持测试快速 - 尽可能并行运行
- 记录复杂的测试设置 - 解释不寻常的模式
评论
支持 Markdown