Communitygithub.com

Jamie-BitFlight/bash-testing

此技能适用于当用户要求“测试 bash 脚本”、“编写 shell 测试”、“使用 shunit2”、“使用 shellspec”、“为 bash 创建测试套件”,或提及单元测试、测试框架、模拟或 shell 脚本的测试驱动开发时。

bash-testing 是什么?

该技能为 shell 脚本开发人员提供了关于 bash 测试框架的全面指导,重点介绍 shunit2 和 shellspec。它涵盖了这两个框架的安装、基本测试结构、断言和匹配器,以及模式,如测试退出代码、标准输出和标准错误、使用固定装置和测试交互功能。该技能还提供了最佳实践和运行测试的命令。它适用于支持 shell 脚本开发的任何平台(CLI、IDE 等),并且通过提供清晰的表格和代码示例,帮助开发人员为他们的 bash 项目选择合适的测试框架并快速上手。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Jamie-BitFlight/claude_skills/tree/main/plugins/bash-development/skills/bash-testing

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Bash 测试

Shell 脚本的测试框架和模式,专注于 shunit2 和 shellspec。

框架选择

框架优势最适合
shunit2xUnit 风格,简单,可移植单元测试,函数测试
shellspecBDD 风格,现代化,功能丰富行为规范,完整项目

shunit2

安装

# 直接下载
curl -L https://github.com/kward/shunit2/raw/master/shunit2 -o shunit2

# 或者通过包管理器
apt install shunit2
brew install shunit2

基本测试结构

#!/usr/bin/env bash

# 引入被测试的脚本
source ./my_script.sh

# 测试函数以 test 开头
test_addition() {
    result=$(add 2 3)
    assertEquals "2 + 3 应等于 5" "5" "$result"
}

test_string_output() {
    result=$(greet "World")
    assertEquals "Hello, World" "$result"
}

test_file_creation() {
    create_temp_file
    assertTrue "临时文件应存在" "[ -f /tmp/testfile ]"
}

test_exit_code() {
    validate_input "valid"
    assertEquals "有效输入应返回 0" 0 $?
}

# 设置与清理
oneTimeSetUp() {
    # 在所有测试前运行一次
    export TEST_DIR=$(mktemp -d)
}

oneTimeTearDown() {
    # 在所有测试后运行一次
    rm -rf "$TEST_DIR"
}

setUp() {
    # 在每个测试前运行
    cd "$TEST_DIR"
}

tearDown() {
    # 在每个测试后运行
    rm -f "$TEST_DIR"/*
}

# 加载 shunit2
source shunit2

shunit2 断言

# 相等性
assertEquals [消息] 期望值 实际值
assertNotEquals [消息] 不应出现的值 实际值

# 空值
assertNull [消息] 值
assertNotNull [消息] 值

# 布尔/状态
assertTrue [消息] 条件
assertFalse [消息] 条件

# 相同引用(字符串比较)
assertSame [消息] 期望值 实际值
assertNotSame [消息] 不应出现的值 实际值

# 包含(在字符串中)
assertContains [消息] 容器 内容

# 退出状态
assertEquals 0 $?

隔离测试函数

代码示例

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

source ./my_functions.sh

test_calculate_sum_empty() {
    result=$(calculate_sum)
    assertEquals "空和应为 0" "0" "$result"
}

test_calculate_sum_single() {
    result=$(calculate_sum 5)
    assertEquals "5" "$result"
}

test_calculate_sum_multiple() {
    result=$(calculate_sum 1 2 3 4 5)
    assertEquals "15" "$result"
}

test_validate_email_valid() {
    validate_email "[email protected]"
    assertTrue "有效邮件应通过" $?
}

test_validate_email_invalid() {
    validate_email "not-an-email"
    assertFalse "无效邮件应失败" $?
}

source shunit2

shellspec

安装

# 通过 curl
curl -fsSL https://git.io/shellspec | sh

# 通过 Homebrew
brew install shellspec

# 通过包管理器
apt install shellspec

目录结构

project/
├── lib/
│   └── functions.sh
├── spec/
│   ├── spec_helper.sh
│   ├── functions_spec.sh
│   └── support/
│       └── fixtures/
└── .shellspec

配置 (.shellspec)

--require spec_helper
--format documentation
--color

基本规范结构

代码示例

shellspec 匹配器

# 输出匹配器
The output should eq "完全匹配"
The output should include "部分匹配"
The output should start with "前缀"
The output should end with "后缀"
The output should match pattern "*通配符*"
The output should be blank

# 状态匹配器
The status should be success    # 退出码 0
The status should be failure    # 退出码非 0
The status should eq 1          # 特定退出码

# 变量匹配器
The variable VAR should eq "值"
The variable VAR should be defined
The variable VAR should be undefined

# 文件匹配器
The file "路径" should be exist
The file "路径" should be file
The file "路径" should be directory
The file "路径" should be readable

# 路径匹配器
The path "文件.txt" should be exist

在 shellspec 中模拟

Describe '部署函数'
  # 模拟外部命令
  curl() {
    echo "模拟响应"
    return 0
  }

  It '调用 API 端点'
    When call deploy "server"
    The output should include "模拟响应"
  End
End

Describe '文件操作'
  # 通过函数覆盖模拟
  Mock rm
    echo "rm 被调用,参数为:$*"
  End

  It '尝试删除文件'
    When call cleanup_temp
    The output should include "rm 被调用,参数为"
  End
End

规范帮助器

# spec/spec_helper.sh

# 加载公共函数
spec_helper_precheck() {
  minimum_version "0.28.0"
}

spec_helper_loaded() {
  # 设置测试环境
  export TEST_MODE=true
}

spec_helper_configure() {
  # 导入项目函数
  import 'lib/functions.sh'
}

测试模式

测试退出码

# shunit2
test_success_exit() {
    run_command "有效输入"
    assertEquals 0 $?
}

test_error_exit() {
    run_command "无效输入"
    assertEquals 1 $?
}

# shellspec
It '成功时退出 0'
  When call run_command "有效输入"
  The status should eq 0
End

测试标准输出和标准错误

# shunit2
test_stdout() {
    result=$(my_function 2>/dev/null)
    assertEquals "期望输出" "$result"
}

test_stderr() {
    error=$(my_function 2>&1 >/dev/null)
    assertContains "$error" "错误消息"
}

# shellspec
It '输出到标准输出'
  When call my_function
  The stdout should eq "期望输出"
End

It '将错误输出到标准错误'
  When call my_function
  The stderr should include "错误消息"
End

使用固定装置测试

# 设置测试固定装置
setUp() {
    TEST_DIR=$(mktemp -d)
    cat > "$TEST_DIR/config.json" <<EOF
{
    "key": "value"
}
EOF
}

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

test_config_parsing() {
    result=$(parse_config "$TEST_DIR/config.json")
    assertEquals "value" "$result"
}

测试交互式函数

# 通过 heredoc 提供输入
test_interactive() {
    result=$(my_prompt <<EOF
yes
EOF
)
    assertEquals "confirmed" "$result"
}

# 或使用 printf
test_with_input() {
    result=$(printf 'yes\n' | my_prompt)
    assertEquals "confirmed" "$result"
}

运行测试

# shunit2
./test_script.sh

# shellspec
shellspec                    # 运行所有规范
shellspec spec/file_spec.sh  # 运行特定规范
shellspec --format tap       # TAP 格式输出
shellspec --jobs 4           # 并行执行

最佳实践

  1. 实际情况下每个测试一个断言
  2. 描述性测试名称,说明测试内容
  3. 隔离测试 - 测试之间无依赖
  4. 测试边界情况 - 空输入、特殊字符、大数据
  5. 在 tearDown 中清理资源
  6. 模拟外部命令 - 不要测试 curl,要测试你的逻辑
  7. 测试退出码而不仅仅是输出

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

Jamie-BitFlight/agentskill-kaizen-meta-docs

Agentskill kaizen plugin documentation index. Load when needing to read about cross-platform notes, improvement plans, or DuckDB integration.

Jamie-BitFlight/bash-51-features

Bash 5.1 release features and improvements with practical examples. Use when working with Bash 5.1 features, epoch time variables, redirection enhancements, or when user asks about Bash 5.1 changes, new features, or version-specific capabilities.

Jamie-BitFlight/bash-52-features

Bash 5.2 release features and improvements with practical examples. Use when working with Bash 5.2 features, variable handling enhancements, readline improvements, or when user asks about Bash 5.2 changes, new features, or version-specific capabilities.

Jamie-BitFlight/bash-53-features

Bash 5.3 release features and improvements with practical examples. Use when working with Bash 5.3 features, new command substitution, GLOBSORT, loadable builtins, or when user asks about Bash 5.3 changes, new features, or version-specific capabilities.

Jamie-BitFlight/bash-development

This skill should be used when the user asks to "write a bash script", "create a shell script", "implement bash function", "parse arguments in bash", "handle errors in bash", or mentions bash development, shell scripting, script templates, or modern bash patterns.

Jamie-BitFlight/bash-lint

This skill should be used when the user asks to "lint bash script", "run shellcheck", "format shell script", "use shfmt", "fix shellcheck errors", or mentions shell script linting, formatting, code quality, or pre-commit hooks for bash.

Jamie-BitFlight/bash-logging

This skill should be used when the user asks to "add logging to bash script", "colorize output", "implement log levels", "CI/CD sections", "terminal colors in bash", or mentions logging functions, emoji output, collapsible CI sections, or shlocksmith.

Jamie-BitFlight/bash-portability

This skill should be used when the user asks about "POSIX compatibility", "portable shell scripts", "cross-shell compatibility", "bashisms", "shebang selection", or mentions writing scripts that work on different shells (bash, sh, dash, zsh) or different systems.

Jamie-BitFlight/brainstorming-skill

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or when users request help with ideation, marketing, and strategic planning. Explores user intent, requirements, and design before implementation using research-validated prompt patterns.

Jamie-BitFlight/clang-format

Configure clang-format code formatting. Use when: user mentions clang-format or .clang-format, analyzing code style/patterns, creating/modifying formatting config, troubleshooting formatting, brace styles/indentation/spacing/alignment/pointer alignment, or codifying conventions.

Jamie-BitFlight/commitlint

When setting up commit message validation for a project. When project has commitlint.config.js or .commitlintrc files. When configuring CI/CD to enforce commit format. When extracting commit rules for LLM prompt generation. When debugging commit message rejection errors.

Jamie-BitFlight/conventional-commits

When writing a git commit message. When task completes and changes need committing. When project uses semantic-release, commitizen, git-cliff. When choosing between feat/fix/chore/docs types. When indicating breaking changes. When generating changelogs from commit history.

Jamie-BitFlight/dasel-reference

Use when querying, modifying, or converting JSON, YAML, TOML, XML, CSV, HCL, or INI with dasel v3. Complete reference for selectors, functions, conditionals, variables, spread operator, type casting, and format-specific patterns.

Jamie-BitFlight/data-exploration

Use when exploring unknown structured data files with dasel v3 — discover schema, list keys, find nested values, sample arrays, identify data types across JSON, YAML, TOML, XML, CSV, HCL, INI formats

Jamie-BitFlight/data-transformation

Use when modifying, converting, or transforming structured data with dasel v3 — in-place mutations, format conversion, batch operations, array manipulation, object construction, and merge patterns across JSON, YAML, TOML, XML, CSV, HCL, INI

Jamie-BitFlight/delegate

Decompose substantive work into phases, dispatch each phase to a sub-agent, and adjudicate what comes back. Use whenever a request asks for implementation, investigation, a fix, a review, or any change to files — including small ones — and whenever you are about to read source or run a diagnostic yourself instead of handing it off. Also use when a report from a sub-agent needs judging, when a phase needs re-dispatching, or when a user names one instance of a pattern. Does not apply when your own prompt begins "Your ROLE_TYPE is sub-agent." — then follow references/sub-agent-contract.md instead.

Jamie-BitFlight/enterprise-hibernate-hbm

Dasel v3 query patterns for Hibernate .hbm.xml mapping files — entity-table binding, Java property-to-column extraction, one-to-many set/list/bag relationship tracing, many-to-one foreign key discovery, batch scanning across 60+ HBM files. Use when querying Hibernate ORM class mappings, extracting schema metadata from Java persistence layer, or auditing entity-column relationships in enterprise legacy codebases.

Jamie-BitFlight/enterprise-installanywhere

Dasel v3 query patterns for InstallAnywhere .iap_xml installer definitions — use when querying action sequences, discovering variables, resolving platform conditions, navigating panels, or comparing installer variants. Files are 2.5+ MB, 65,000+ lines — too large for context reads, requires structural dasel queries.

Jamie-BitFlight/enterprise-maven-pom

Dasel v3 selector patterns for Maven POM XML files — use when querying dependency versions, filtering by groupId or scope, extracting module hierarchy from parent POMs, or detecting version conflicts across enterprise multi-module Java projects. Load this skill when working with pom.xml files using dasel.

Jamie-BitFlight/enterprise-spring-xml

Dasel v3 selectors for Spring bean factory XML — use when querying any Spring ApplicationContext XML for bean discovery, dependency wiring, JMS destination mapping, property injection extraction, or cross-bean reference tracing. Load this skill before writing dasel selectors against Spring bean XML files (applicationContext.xml, *_beans.xml, spring-*.xml).

相关技能