File contents 何时使用
该用 :给 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 许可)。
1 --- 2 name: bats-shell-testing 3 description: 何时使用 4 --- 5 ## 何时使用 6 7 - **该用**:给 shell 脚本/CLI 工具写单元测试;对脚本做 TDD(先写 `@test` 再实现);在 CI/CD 中接入自动化脚本测试;覆盖边界与错误分支(缺参、文件不存在、权限拒绝、非法选项);验证脚本在 bash/sh/dash 等多种 shell 下行为一致。 8 - **不该用(负边界)**:项目根本不含 shell 脚本;需要跨服务/真实环境的集成测试(Bats 只测 shell 层行为);目标只是 lint 或格式化;只想做静态检查——那用 shellcheck(见互见),它不替代运行时测试。 9 10 ## 步骤 11 12 1. **装 Bats 并确认目标 shell**:`brew install bats-core` / `npm i -g bats` / 源码 `./install.sh /usr/local`;`bats --version` 验证。先确认要支持的 shell 方言与环境。 13 2. **搭测试结构**:脚本放 `bin/`,测试放 `tests/*.bats`,夹具放 `tests/fixtures/`,共享工具放 `tests/test_helper.sh`(用 `load test_helper` 引入)。 14 3. **写测试三类断言**:退出码(`$status`)、输出(`$output` / `${lines[N]}`)、副作用(文件是否生成/内容/权限)。每个测试只验一件事,命名清楚说明意图。 15 4. **加 setup/teardown**:`setup` 建临时目录与夹具,`teardown` 清理;昂贵的一次性准备用 `setup_file`/`teardown_file`。 16 5. **隔离外部依赖**:mock 函数或在 `PATH` 前置 stub 目录拦截 `curl`/`jq` 等命令;缺依赖用 `skip`。 17 6. **跑测试并接 CI**:本地 `bats tests/*.bats`(`--tap` 出 TAP、`--parallel N` 并行),在 GitHub Actions / Makefile 中固化。 18 19 ## 指令 20 21 **核心 API(背下来)** 22 23 - `run cmd` 执行命令并捕获结果 → 读 `$status`(退出码)、`$output`(全部输出)、`${lines[i]}`(按行)。 24 - `@test "描述" { ... }` 定义一个测试;测试体内任一 `[ ... ]` 失败即整测试失败。 25 - `setup`/`teardown` 每个测试前后各跑一次;`setup_file`/`teardown_file` 整文件一次。 26 - `load 文件名` 引入 helper;`skip "原因"` 跳过;`${BATS_TEST_DIRNAME}` 指向当前 .bats 所在目录。 27 28 **断言惯用法** 29 30 - 退出码:`[ "$status" -eq 0 ]` / `[ "$status" -ne 0 ]` / 指定码 `[ "$status" -eq 127 ]`。 31 - 输出相等/含子串/正则:`[ "$output" = "expected" ]` / `[[ "$output" == *"world"* ]]` / `[[ "$output" =~ ^[0-9]{4}$ ]]`。 32 - 文件副作用:`[ -f file ]`、`[ "$(cat file)" = "..." ]`、`[ "$(wc -c < file)" -eq 5 ]`。 33 34 **夹具与隔离** 35 36 - 临时目录:`setup() { TEST_DIR=$(mktemp -d); export TEST_DIR; }` + `teardown() { rm -rf "$TEST_DIR"; }`,绝不污染工作区。 37 - 命令 stub:把可执行假命令写进 `$STUBS_DIR` 并 `export PATH="$STUBS_DIR:$PATH"`,控制其输出与退出码。 38 - 函数 mock:重定义同名函数 + `export -f`,让被测脚本调到假实现。 39 40 ## 示例 41 42 最小测试文件(夹具 + 三类断言): 43 44 ```bash 45 #!/usr/bin/env bats 46 load test_helper 47 48 setup() { TMPDIR=$(mktemp -d); export TMPDIR; } 49 teardown() { rm -rf "$TMPDIR"; } 50 51 @test "成功时返回 0" { 52 run my_function "input" 53 [ "$status" -eq 0 ] 54 } 55 56 @test "缺参时报错并提示 Usage" { 57 run my_function 58 [ "$status" -ne 0 ] 59 [[ "$output" == *"Usage:"* ]] 60 } 61 62 @test "生成输出文件且内容正确" { 63 my_function > "$TMPDIR/out.txt" 64 [ -f "$TMPDIR/out.txt" ] 65 [ "$(cat "$TMPDIR/out.txt")" = "expected content" ] 66 } 67 ``` 68 69 命令打桩(拦截外部 `curl`): 70 71 ```bash 72 create_stub() { # 在 $STUBS_DIR 生成假命令 73 cat > "$STUBS_DIR/$1" <<EOF 74 #!/bin/bash 75 echo "$2" 76 exit ${3:-0} 77 EOF 78 chmod +x "$STUBS_DIR/$1" 79 } 80 81 @test "API 调用走桩" { 82 create_stub curl '{ "status": "ok" }' 0 83 run my_api_function 84 [ "$status" -eq 0 ] 85 } 86 ``` 87 88 依赖缺失时跳过 + 多 shell 兼容: 89 90 ```bash 91 @test "JSON 解析" { 92 command -v jq >/dev/null || skip "jq 未安装" 93 run my_json_parser '{"key":"value"}' 94 [ "$status" -eq 0 ] 95 } 96 97 @test "脚本在 POSIX sh 下可运行" { 98 sh "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1 99 } 100 ``` 101 102 CI 接入(GitHub Actions 片段): 103 104 ```yaml 105 - name: Install Bats 106 run: npm install --global bats 107 - name: Run Tests 108 run: bats tests/*.bats --tap | tee test_output.tap 109 ``` 110 111 ## 注意事项 112 113 - **务必清理**:临时文件/目录一律在 `teardown` 中 `rm -rf`,否则测试间相互污染。改了权限做完即复原(如 `chmod 000` 测完 `chmod 644`)。 114 - **测好失败路径**:别只测 happy path——缺参、`/nonexistent` 文件、空输入、权限拒绝、非法选项都要覆盖,并断言错误信息(`*"not found"*`、`*"Usage:"*`)。 115 - **`run` 的边界**:`run` 会吞掉退出码(命令失败不会让测试自动失败),必须显式断言 `$status`;不需要捕获时也可直接跑命令让其非零退出令测试失败。 116 - **隔离单元**:mock/stub 外部命令,别在单测里打真实网络/数据库;复杂数据用 fixtures 文件提升可读性。 117 - **可移植性**:`stat -f`、`echo -e`、`{1..10}` 等并非各 shell 通用;要跨 dash/ash 验证就在对应 shell 实跑(容器:`alpine`=ash、`debian`=dash)。 118 - **速度**:测试要快,独立用例用 `bats --parallel N` 并行;不寻常的 setup 写注释说明。 119 120 ## 互见 121 122 - requires:`bash-defensive-patterns` —— 先会写健壮 shell 脚本,才谈得上为其编写有意义的测试。 123 - related:`posix-shell-scripting`(被测脚本若要可移植,配套用 sh 方言测试)、`shellcheck-linting`(静态检查与 Bats 运行时测试互补,二者都进 pre-commit)。 124 - combines_with:`ci-cd-pipeline-builder` —— 把 `bats tests/*.bats --tap` 接入流水线,回归早发现。 125 - 参考:Bats-core 仓库 github.com/bats-core/bats-core、文档 bats-core.readthedocs.io、TAP 协议 testanything.org。 126 127 --- 128 采编自 sickn33/antigravity-awesome-skills(MIT 许可)。
findscripter/everything-skills/tree/main/02-engineering/bats-shell-testing commit 7cbeb782ce
Frequently asked questions How do I install the Bats Shell Testing skill? Run npx skillmds@latest add findscripter/bats-shell-testing in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
What does the Bats Shell Testing skill do? 何时使用 It is listed under Coding & Dev Tools on SkillMD.
Is Bats Shell Testing safe to use? This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
Which AI agents work with Bats Shell Testing? This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Is Bats Shell Testing free to use? Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
Who published Bats Shell Testing? findscripter (@findscripter) published this skill. Their other Agent Skills are listed on their SkillMD profile.