Dev-Control uses bats-core for testing. Tests are organised by component:
tests/
├── dc.bats # Integration tests for the `dc` entrypoint (help, --version, status JSON)
├── plugin.bats # Tests for plugin discovery via `dc plugin list/info`
├── run_tests.sh # Test runner script (bootstraps bats if missing)
├── lib/ # Unit tests for the shared library modules
│ ├── cli.bats # scripts/lib/cli.sh + scripts/lib/validation.sh
│ ├── config.bats # scripts/lib/config.sh (YAML parser, dc_config, dc_config_set)
│ ├── git_utils.bats # scripts/lib/git/utils.sh (parse_github_url, branch/remote helpers, ...)
│ └── output.bats # scripts/lib/output.sh (out, verbose, json_field, parse_output_flags)
└── test_helper/ # bats plugins (auto-installed by run_tests.sh)
├── bats/
├── bats-support/
└── bats-assert/
./tests/run_tests.sh# Library tests only
./tests/run_tests.sh lib/
# Specific test file
./tests/run_tests.sh dc.bats
# Specific test by name
bats --filter "dc --help" tests/dc.bats# Install kcov first
sudo apt install kcov
# Run with coverage
kcov --include-path=./scripts coverage/ ./tests/run_tests.sh#!/usr/bin/env bats
load 'test_helper/bats-support/load'
load 'test_helper/bats-assert/load'
setup() {
# Runs before each test
source "$BATS_TEST_DIRNAME/../scripts/lib/cli.sh"
}
teardown() {
# Runs after each test
rm -rf "$TEST_TEMP_DIR"
}
@test "description of what is being tested" {
run my_function "arg1" "arg2"
assert_success
assert_output "expected output"
}# Status assertions
assert_success # Exit code 0
assert_failure # Non-zero exit code
# Output assertions
assert_output "exact" # Exact match
assert_output --partial "contains" # Substring
assert_output --regexp "pattern.*" # Regex
# Comparison
assert_equal "$actual" "expected"
# File assertions
assert [ -f "$file" ] # File exists
assert [ -d "$dir" ] # Directory exists@test "to_slug lowercases and hyphenates" {
result=$(to_slug "My Project Name")
assert_equal "$result" "my-project-name"
}@test "dc init creates config" {
local temp_dir=$(mktemp -d)
cd "$temp_dir"
git init --quiet
run "$DC" init
assert_success
assert [ -f ".dc-init.yaml" ]
rm -rf "$temp_dir"
}Tests run automatically on push via GitHub Actions:
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: ./tests/run_tests.sh- Isolate tests: Use temp directories, reset globals
- Test one thing: Each test should verify a single behaviour
- Use descriptive names:
@test "dc init with --licence creates LICENCE" - Clean up: Always remove temp files in teardown
- Test edge cases: Empty inputs, missing files, invalid args