Add Assertion Skill
Add a new assertion function to bashunit following strict TDD methodology.
When to Use
Invoke with /add-assertion when:
- Adding a new assertion to the framework
- User requests a new
assert_* function
- Enhancing existing assertion capabilities
Prerequisites
- Task file must exist - Create
.tasks/YYYY-MM-DD-add-assertion-name.md
- Understand patterns - Study
tests/unit/assert_test.sh
- Know the requirement - What should the assertion do?
Workflow
1. Planning Phase
Ask user:
- What should the assertion check?
- What should it be called? (e.g.,
assert_json_contains)
- What parameters does it take?
- What should success look like?
- What should failure messages say?
Document in task file:
markdown
1# Add assert_json_contains
2
3## Context
4Need assertion to verify JSON contains a specific key-value pair.
5
6## Acceptance Criteria
7- [ ] Function validates JSON structure
8- [ ] Function checks for key existence
9- [ ] Function checks value matches
10- [ ] Clear error messages on failure
11- [ ] Works with nested JSON
12- [ ] Handles malformed JSON gracefully
13
14## Test Inventory
15### Unit Tests
16- [ ] test_assert_json_contains_should_pass_when_key_value_exists
17- [ ] test_assert_json_contains_should_fail_when_key_missing
18- [ ] test_assert_json_contains_should_fail_when_value_differs
19- [ ] test_assert_json_contains_should_handle_nested_keys
20- [ ] test_assert_json_contains_should_fail_on_malformed_json
2. Study Existing Patterns
Read assertion patterns:
bash
1# Study how other assertions work
2Read tests/unit/assert_test.sh
3Read src/assertions.sh
Identify pattern:
- Parameter validation
- Assertion logic
- Error message format
- Success/failure return codes
- Testing approach (including failure tests)
3. TDD Cycle 1: Basic Success Case
RED
bash
1# tests/unit/assert_json_contains_test.sh
2
3function test_assert_json_contains_should_pass_when_key_value_exists() {
4 local json='{"name": "bashunit", "version": "0.32.0"}'
5
6 assert_json_contains "name" "bashunit" "$json"
7}
Run test - MUST FAIL (function doesn't exist):
bash
1./bashunit tests/unit/assert_json_contains_test.sh
GREEN
bash
1# src/assert_json.sh (or add to src/assertions.sh)
2
3function assert_json_contains() {
4 local key="$1"
5 local expected_value="$2"
6 local json="$3"
7
8 # Minimal implementation
9 local actual_value
10 actual_value=$(echo "$json" | grep -o "\"$key\": *\"[^\"]*\"" | cut -d'"' -f4)
11
12 if [[ "$actual_value" == "$expected_value" ]]; then
13 return 0
14 fi
15
16 echo "Expected key '$key' to have value '$expected_value', got '$actual_value'" >&2
17 return 1
18}
Run test - MUST PASS:
bash
1./bashunit tests/unit/assert_json_contains_test.sh
REFACTOR
Improve implementation:
- Better JSON parsing
- Error handling
- Documentation
4. TDD Cycle 2: Failure Case
RED
bash
1function test_assert_json_contains_should_fail_when_key_missing() {
2 local json='{"name": "bashunit"}'
3
4 # Use assert_fails to test that the assertion fails
5 assert_fails \
6 "assert_json_contains 'version' '0.32.0' '$json'"
7}
GREEN
Update implementation to handle missing keys:
bash
1function assert_json_contains() {
2 local key="$1"
3 local expected_value="$2"
4 local json="$3"
5
6 # Check if key exists
7 if ! echo "$json" | grep -q "\"$key\""; then
8 echo "Key '$key' not found in JSON" >&2
9 return 1
10 fi
11
12 local actual_value
13 actual_value=$(echo "$json" | grep -o "\"$key\": *\"[^\"]*\"" | cut -d'"' -f4)
14
15 if [[ "$actual_value" == "$expected_value" ]]; then
16 return 0
17 fi
18
19 echo "Expected key '$key' to have value '$expected_value', got '$actual_value'" >&2
20 return 1
21}
REFACTOR
Continue improving.
5. TDD Cycle 3-N: Additional Cases
Continue RED-GREEN-REFACTOR for:
- Different value types (numbers, booleans)
- Nested JSON objects
- Malformed JSON handling
- Edge cases (empty values, null, etc.)
6. Documentation
Add function documentation:
bash
1##
2# Asserts that a JSON string contains a specific key-value pair
3#
4# Arguments:
5# $1 - Key to search for
6# $2 - Expected value
7# $3 - JSON string
8#
9# Returns:
10# 0 if key exists with expected value
11# 1 if key missing or value differs
12#
13# Example:
14# local json='{"name": "bashunit"}'
15# assert_json_contains "name" "bashunit" "$json"
16##
17function assert_json_contains() {
18 # Implementation
19}
7. Integration
If new file created:
- Source it in main
bashunit.sh
- Add to build process if needed
Update exports:
bash
1# In src/bashunit.sh or relevant file
2export -f assert_json_contains
8. Comprehensive Testing
Run all assertion tests:
bash
1./bashunit tests/unit/assert*.sh
Run full test suite:
Quality checks:
bash
1make sa
2make lint
3shfmt -w .
9. Documentation Updates
Update user-facing docs:
- Add to
docs/assertions.md (or equivalent)
- Add examples
- Add to API reference
- Update CHANGELOG
Example docs:
markdown
1## assert_json_contains
2
3Asserts that a JSON string contains a specific key-value pair.
4
5### Usage
6
7bash
8assert_json_contains "key" "expected_value" "$json_string"
9
10
11### Parameters
12
13- `key` - The JSON key to look for
14- `expected_value` - The expected value for the key
15- `json_string` - The JSON to search
16
17### Examples
18
19bash
20# Simple object
21local user='{"name": "John", "age": 30}'
22assert_json_contains "name" "John" "$user"
23
24# Nested object
25local data='{"user": {"name": "John"}}'
26assert_json_contains "user.name" "John" "$data"
27
10. Final Verification
Complete checklist:
Testing Patterns for Assertions
Test Success
bash
1function test_should_pass_when_condition_met() {
2 assert_new_assertion "input"
3}
Test Failure
bash
1function test_should_fail_when_condition_not_met() {
2 assert_fails \
3 "assert_new_assertion 'bad_input'"
4}
Test Error Message
bash
1function test_should_show_helpful_error_message() {
2 local output
3 output=$(assert_new_assertion "bad_input" 2>&1) || true
4
5 assert_contains "Expected" "$output"
6 assert_contains "Got" "$output"
7}
Test Edge Cases
bash
1function test_should_handle_empty_input() {
2 assert_fails \
3 "assert_new_assertion ''"
4}
5
6function test_should_handle_special_characters() {
7 assert_new_assertion "special$chars"
8}
Common Assertion Patterns
Parameter Validation
bash
1if [[ $# -lt 2 ]]; then
2 echo "assert_name requires at least 2 arguments" >&2
3 return 1
4fi
Clear Error Messages
bash
1echo "Expected <expected>, but got <actual>" >&2
2echo " Expected: $expected" >&2
3echo " Actual: $actual" >&2
Consistent Return Codes
bash
1return 0 # Success
2return 1 # Assertion failed
assert_equals - Basic comparison
assert_contains - String searching
assert_matches - Regex matching
assert_file_exists - File operations
assert_array_contains - Array operations
Provide progress updates:
📝 Planning: assert_json_contains function
- Parameters: key, expected_value, json_string
- Will validate JSON and check key-value
🔴 RED: test_should_pass_when_key_value_exists
Test written and failing (function not found)
🟢 GREEN: Minimal implementation added
Test now passing
🔵 REFACTOR: Improved JSON parsing
All tests still passing
✅ Completed: assert_json_contains
- 5 tests passing
- Documentation added
- Ready for review
Quality Standards
All assertions must:
- Follow Bash 3.0+ compatibility (@.claude/rules/bash-style.md)
- Have comprehensive tests (success, failure, edge cases)
- Have clear, helpful error messages
- Be documented with examples
- Pass shellcheck and formatting
- Work in parallel test execution
- Test patterns: @tests/unit/assert_test.sh
- Assertion implementations: @src/assertions.sh
- Testing rules: @.claude/rules/testing.md
- TDD workflow: @.claude/rules/tdd-workflow.md