bats-testing-patterns
Comprehensive guide for writing shell script tests using Bats (Bash Automated Testing System). Use when writing or improving tests for Bash/shell scripts, creating test fixtures, mocking commands, or setting up CI/CD for shell script testing. Includes patterns for assertions, setup/teardown, mocking, fixtures, and integration with GitHub Actions.
What this skill does
# Bats Testing Patterns
## Overview
Bats (Bash Automated Testing System) provides a TAP-compliant testing framework for shell scripts. This skill documents proven patterns for writing effective, maintainable shell script tests that catch bugs early and document expected behavior.
**Use this skill when:**
- Writing tests for Bash or shell scripts
- Creating test fixtures and mock data for shell testing
- Setting up test infrastructure for shell-based tools
- Debugging failing shell tests
- Integrating shell tests into CI/CD pipelines
## Core Testing Patterns
### Basic Test Structure
Every Bats test file is a shell script with a `.bats` extension:
```bash
#!/usr/bin/env bats
@test "Test description goes here" {
# Test code
[ condition ]
}
```
**Key Points:**
- Use descriptive test names that explain what is being verified
- Each `@test` block is an independent test
- Tests should be focused on one specific behavior
- Use the shebang `#!/usr/bin/env bats` at the top
### Exit Code Assertions
Test command success and failure explicitly:
```bash
#!/usr/bin/env bats
@test "Command succeeds as expected" {
run echo "hello"
[ "$status" -eq 0 ]
}
@test "Command fails as expected" {
run false
[ "$status" -ne 0 ]
}
@test "Command returns specific exit code" {
run bash -c "exit 127"
[ "$status" -eq 127 ]
}
@test "Can capture command result" {
run echo "hello"
[ $status -eq 0 ]
[ "$output" = "hello" ]
}
```
**Best Practice:** Always use `run` to capture command output and exit status. The `run` command sets `$status`, `$output`, and `$lines` variables for assertions.
### Output Assertions
Verify command output matches expectations:
```bash
#!/usr/bin/env bats
@test "Output matches exact string" {
result=$(echo "hello world")
[ "$result" = "hello world" ]
}
@test "Output contains substring" {
result=$(echo "hello world")
[[ "$result" == *"world"* ]]
}
@test "Output matches regex pattern" {
result=$(date +%Y)
[[ "$result" =~ ^[0-9]{4}$ ]]
}
@test "Multi-line output comparison" {
run printf "line1\nline2\nline3"
[ "$output" = "line1
line2
line3" ]
}
@test "Using lines array for output" {
run printf "line1\nline2\nline3"
[ "${lines[0]}" = "line1" ]
[ "${lines[1]}" = "line2" ]
[ "${lines[2]}" = "line3" ]
[ "${#lines[@]}" -eq 3 ]
}
```
**Tip:** Use the `$lines` array when testing multi-line output - it's cleaner than string comparison.
### File Assertions
Test file operations and attributes:
```bash
#!/usr/bin/env bats
setup() {
TEST_DIR=$(mktemp -d)
export TEST_DIR
}
teardown() {
rm -rf "$TEST_DIR"
}
@test "File is created successfully" {
[ ! -f "$TEST_DIR/output.txt" ]
echo "content" > "$TEST_DIR/output.txt"
[ -f "$TEST_DIR/output.txt" ]
}
@test "File contents match expected" {
echo "expected content" > "$TEST_DIR/output.txt"
[ "$(cat "$TEST_DIR/output.txt")" = "expected content" ]
}
@test "File is readable" {
touch "$TEST_DIR/test.txt"
[ -r "$TEST_DIR/test.txt" ]
}
@test "File has correct permissions (Linux)" {
touch "$TEST_DIR/test.txt"
chmod 644 "$TEST_DIR/test.txt"
[ "$(stat -c %a "$TEST_DIR/test.txt")" = "644" ]
}
@test "File has correct permissions (macOS)" {
touch "$TEST_DIR/test.txt"
chmod 644 "$TEST_DIR/test.txt"
[ "$(stat -f %OLp "$TEST_DIR/test.txt")" = "644" ]
}
@test "File size is correct" {
echo -n "12345" > "$TEST_DIR/test.txt"
[ "$(wc -c < "$TEST_DIR/test.txt")" -eq 5 ]
}
@test "Directory structure is created" {
mkdir -p "$TEST_DIR/sub/nested/deep"
[ -d "$TEST_DIR/sub/nested/deep" ]
}
```
**Platform Note:** File permission checking differs between Linux (`stat -c`) and macOS (`stat -f`). Test on your target platform or provide compatibility helpers.
## Setup and Teardown Patterns
### Basic Setup and Teardown
Execute code before and after each test:
```bash
#!/usr/bin/env bats
setup() {
# Runs before EACH test
TEST_DIR=$(mktemp -d)
export TEST_DIR
# Source the script under test
source "${BATS_TEST_DIRNAME}/../bin/script.sh"
}
teardown() {
# Runs after EACH test
rm -rf "$TEST_DIR"
}
@test "Test using TEST_DIR" {
touch "$TEST_DIR/file.txt"
[ -f "$TEST_DIR/file.txt" ]
}
@test "Second test has clean TEST_DIR" {
# TEST_DIR is recreated fresh for each test
[ ! -f "$TEST_DIR/file.txt" ]
}
```
**Critical:** The `setup()` and `teardown()` functions run before and after EACH test, ensuring test isolation.
### Setup with Test Resources
Create fixtures and test data:
```bash
#!/usr/bin/env bats
setup() {
# Create directory structure
TEST_DIR=$(mktemp -d)
mkdir -p "$TEST_DIR/data/input"
mkdir -p "$TEST_DIR/data/output"
# Create test fixtures
echo "line1" > "$TEST_DIR/data/input/file1.txt"
echo "line2" > "$TEST_DIR/data/input/file2.txt"
echo "line3" > "$TEST_DIR/data/input/file3.txt"
# Initialize environment variables
export DATA_DIR="$TEST_DIR/data"
export INPUT_DIR="$DATA_DIR/input"
export OUTPUT_DIR="$DATA_DIR/output"
# Source the script being tested
source "${BATS_TEST_DIRNAME}/../scripts/process_files.sh"
}
teardown() {
rm -rf "$TEST_DIR"
}
@test "Processes all input files" {
process_files "$INPUT_DIR" "$OUTPUT_DIR"
[ -f "$OUTPUT_DIR/file1.txt" ]
[ -f "$OUTPUT_DIR/file2.txt" ]
[ -f "$OUTPUT_DIR/file3.txt" ]
}
@test "Handles empty input directory" {
rm -rf "$INPUT_DIR"/*
process_files "$INPUT_DIR" "$OUTPUT_DIR"
[ "$(ls -A "$OUTPUT_DIR")" = "" ]
}
```
### Global Setup/Teardown
Run expensive setup once for all tests:
```bash
#!/usr/bin/env bats
# Load shared test utilities
load test_helper
# setup_file runs ONCE before all tests in the file
setup_file() {
export SHARED_RESOURCE=$(mktemp -d)
export SHARED_DB="$SHARED_RESOURCE/test.db"
# Expensive operation: initialize database
echo "Creating test database..."
sqlite3 "$SHARED_DB" < "${BATS_TEST_DIRNAME}/fixtures/schema.sql"
}
# teardown_file runs ONCE after all tests in the file
teardown_file() {
rm -rf "$SHARED_RESOURCE"
}
# setup runs before each test (optional)
setup() {
# Per-test setup if needed
export TEST_ID=$(date +%s%N)
}
@test "First test uses shared resource" {
[ -f "$SHARED_DB" ]
sqlite3 "$SHARED_DB" "SELECT COUNT(*) FROM users;"
}
@test "Second test uses same shared resource" {
[ -f "$SHARED_DB" ]
# Database persists between tests
sqlite3 "$SHARED_DB" "INSERT INTO users (name) VALUES ('test_$TEST_ID');"
}
```
**Use Case:** Global setup/teardown is perfect for expensive operations like database initialization, server startup, or large file downloads that can be shared across tests.
## Mocking and Stubbing Patterns
### Function Mocking
Override functions for testing:
```bash
#!/usr/bin/env bats
# Mock external command
curl() {
echo '{"status": "success", "data": "mocked"}'
return 0
}
@test "Function uses mocked curl" {
export -f curl
# Source script that calls curl
source "${BATS_TEST_DIRNAME}/../scripts/api_client.sh"
result=$(fetch_data "https://api.example.com/data")
[[ "$result" == *"mocked"* ]]
}
@test "Mock can simulate failure" {
curl() {
echo "Connection refused"
return 1
}
export -f curl
source "${BATS_TEST_DIRNAME}/../scripts/api_client.sh"
run fetch_data "https://api.example.com/data"
[ "$status" -ne 0 ]
}
```
### Command Stubbing with PATH Manipulation
Create stub commands that override system commands:
```bash
#!/usr/bin/env bats
setup() {
# Create stub directory
STUBS_DIR="$BATS_TEST_TMPDIR/stubs"
mkdir -p "$STUBS_DIR"
# Prepend to PATH so stubs are found first
export PATH="$STUBS_DIR:$PATH"
}
teardown() {
rm -rf "$STUBS_DIR"
}
create_stub() {
local cmd="$1"
local output="$2"
local exit_code="${3:-0}"
cat > "$STUBS_DIR/$cmRelated in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.