Bats shell testing
Skill findscripter/everything-skills/02-engineering/bats-shell-testing
当为 Bash/POSIX shell 脚本、CLI 工具或 CI 流程写单元测试、做 TDD 时使用;做 Bats(Bash Automated Testing System)测试编写——断言退出码/输出/文件副作用、setup/teardown 夹具、命令打桩(stub)、CI 接入,产出 .bats 测试套件与 TAP 报告;不适用于非 shell 项目、跨服务集成测试、仅做 lint/格式化或纯 shellcheck 静态检查;触发词:bats、shell 脚本测试、@test、TAP、脚本 TDDFrom its SKILL.md
npx -y skills add findscripter/everything-skills --skill bats-shell-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.0 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
何时使用
- 该用:给 shell 脚本/CLI 工具写单元测试;对脚本做 TDD(先写
@test再实现);在 CI/CD 中接入自动化脚本测试;覆盖边界与错误分支(缺参、文件不存在、权限拒绝、非法选项);验证脚本在 bash/sh/dash 等多种 shell 下行为一致。 - 不该用(负边界):项目根本不含 shell 脚本;需要跨服务/真实环境的集成测试(Bats 只测 shell 层行为);目标只是 lint 或格式化;只想做静态检查——那用 shellcheck(见互见),它不替代运行时测试。
步骤
- 装 Bats 并确认目标 shell:
brew install bats-core/npm i -g bats/ 源码./install.sh /usr/local;bats --version验证。先确认要支持的 shell 方言与环境。 - 搭测试结构:脚本放
bin/,测试放tests/*.bats,夹具放tests/fixtures/,共享工具放tests/test_helper.sh(用load test_helper引入)。 - 写测试三类断言:退出码(
$status)、输出($output/${lines[N]})、副作用(文件是否生成/内容/权限)。每个测试只验一件事,命名清楚说明意图。 - 加 setup/teardown:
setup建临时目录与夹具,teardown清理;昂贵的一次性准备用setup_file/teardown_file。 - 隔离外部依赖:mock 函数或在
PATH前置 stub 目录拦截curl/jq等命令;缺依赖用skip。 - 跑测试并接 CI:本地
bats tests/*.bats(--tap出 TAP、--parallel N并行),在 GitHub Actions / Makefile 中固化。
指令
核心 API(背下来)
run cmd执行命令并捕获结果 → 读$status(退出码)、$output(全部输出)、${lines[i]}(按行)。@test "描述" { ... }定义一个测试;测试体内任一[ ... ]失败即整测试失败。setup/teardown每个测试前后各跑一次;setup_file/teardown_file整文件一次。load 文件名引入 helper;skip "原因"跳过;${BATS_TEST_DIRNAME}指向当前 .bats 所在目录。
断言惯用法
- 退出码:
[ "$status" -eq 0 ]/[ "$status" -ne 0 ]/ 指定码[ "$status" -eq 127 ]。 - 输出相等/含子串/正则:
[ "$output" = "expected" ]/[[ "$output" == *"world"* ]]/[[ "$output" =~ ^[0-9]{4}$ ]]。 - 文件副作用:
[ -f file ]、[ "$(cat file)" = "..." ]、[ "$(wc -c < file)" -eq 5 ]。
夹具与隔离
- 临时目录:
setup() { TEST_DIR=$(mktemp -d); export TEST_DIR; }+teardown() { rm -rf "$TEST_DIR"; },绝不污染工作区。 - 命令 stub:把可执行假命令写进
$STUBS_DIR并export PATH="$STUBS_DIR:$PATH",控制其输出与退出码。 - 函数 mock:重定义同名函数 +
export -f,让被测脚本调到假实现。
示例
最小测试文件(夹具 + 三类断言):
#!/usr/bin/env bats
load test_helper
setup() { TMPDIR=$(mktemp -d); export TMPDIR; }
teardown() { rm -rf "$TMPDIR"; }
@test "成功时返回 0" {
run my_function "input"
[ "$status" -eq 0 ]
}
@test "缺参时报错并提示 Usage" {
run my_function
[ "$status" -ne 0 ]
[[ "$output" == *"Usage:"* ]]
}
@test "生成输出文件且内容正确" {
my_function > "$TMPDIR/out.txt"
[ -f "$TMPDIR/out.txt" ]
[ "$(cat "$TMPDIR/out.txt")" = "expected content" ]
}
命令打桩(拦截外部 curl):
create_stub() { # 在 $STUBS_DIR 生成假命令
cat > "$STUBS_DIR/$1" <<EOF
#!/bin/bash
echo "$2"
exit ${3:-0}
EOF
chmod +x "$STUBS_DIR/$1"
}
@test "API 调用走桩" {
create_stub curl '{ "status": "ok" }' 0
run my_api_function
[ "$status" -eq 0 ]
}
依赖缺失时跳过 + 多 shell 兼容:
@test "JSON 解析" {
command -v jq >/dev/null || skip "jq 未安装"
run my_json_parser '{"key":"value"}'
[ "$status" -eq 0 ]
}
@test "脚本在 POSIX sh 下可运行" {
sh "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
}
CI 接入(GitHub Actions 片段):
- name: Install Bats
run: npm install --global bats
- name: Run Tests
run: bats tests/*.bats --tap | tee test_output.tap
注意事项
- 务必清理:临时文件/目录一律在
teardown中rm -rf,否则测试间相互污染。改了权限做完即复原(如chmod 000测完chmod 644)。 - 测好失败路径:别只测 happy path——缺参、
/nonexistent文件、空输入、权限拒绝、非法选项都要覆盖,并断言错误信息(*"not found"*、*"Usage:"*)。 run的边界:run会吞掉退出码(命令失败不会让测试自动失败),必须显式断言$status;不需要捕获时也可直接跑命令让其非零退出令测试失败。- 隔离单元:mock/stub 外部命令,别在单测里打真实网络/数据库;复杂数据用 fixtures 文件提升可读性。
- 可移植性:
stat -f、echo -e、{1..10}等并非各 shell 通用;要跨 dash/ash 验证就在对应 shell 实跑(容器:alpine=ash、debian=dash)。 - 速度:测试要快,独立用例用
bats --parallel N并行;不寻常的 setup 写注释说明。
互见
- requires:
bash-defensive-patterns—— 先会写健壮 shell 脚本,才谈得上为其编写有意义的测试。 - related:
posix-shell-scripting(被测脚本若要可移植,配套用 sh 方言测试)、shellcheck-linting(静态检查与 Bats 运行时测试互补,二者都进 pre-commit)。 - combines_with:
ci-cd-pipeline-builder—— 把bats tests/*.bats --tap接入流水线,回归早发现。 - 参考:Bats-core 仓库 github.com/bats-core/bats-core、文档 bats-core.readthedocs.io、TAP 协议 testanything.org。
采编自 sickn33/antigravity-awesome-skills(MIT 许可)。
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.