Testing Domain Skill
TDD Cycle: RED -> GREEN -> REFACTOR
- RED - Write a failing test. Must fail for the right reason.
- GREEN - Write minimum code to pass. No gold plating.
- REFACTOR - Improve design. Tests stay green.
# RED
def test_user_full_name():
user = User(first="Jane", last="Doe")
assert user.full_name() == "Jane Doe"
# GREEN
class User:
def __init__(self, first, last):
self.first = first
self.last = last
def full_name(self):
return f"{self.first} {self.last}"
# REFACTOR: tests pass, clean up if needed
See: references/tdd-deep-dive.md for advanced TDD techniques.
What to Test / What NOT to Test
DO test (behavior):
- Public API contracts
- Edge cases and boundaries
- Error conditions
- State transitions
- Business logic
DON'T test (implementation):
- Private methods (test through public API)
- Language features
- Third-party libraries (only verify integration)
- Trivial getters/setters
- Generated code, migrations, static config
Test Types
Unit Tests
- Run in milliseconds, no I/O, no external dependencies
- Deterministic, parallelizable
- Test single units of behavior
Integration Tests
- Seconds to run, may involve I/O
- Test real component boundaries
- Run sequentially if stateful
E2E Tests
- Slowest (seconds to minutes)
- Test complete user flows from user perspective
- Run against staging/test environment
Characterization Tests
- Capture existing behavior of legacy code before refactoring
- Document current behavior (even if wrong), then refactor against it
See: references/test-patterns-by-language.md for language-specific frameworks and idioms.
Test Structure: Arrange-Act-Assert
def test_shopping_cart_total():
# Arrange
cart = ShoppingCart()
cart.add_item(Item("Book", 10.00))
# Act
total = cart.calculate_total()
# Assert
assert total == 10.00
Prefer one assertion per test. Exception: related assertions on the same object.
Coverage Requirements
| Metric |
Threshold |
| Line coverage |
80% minimum (CI-enforced) |
| Branch coverage |
More important than line coverage |
| Critical paths |
100% |
Coverage Commands
| Language |
Command |
| Python |
pytest --cov=myapp --cov-report=html --cov-fail-under=80 |
| JavaScript |
jest --coverage --coverageThreshold='{"global":{"lines":80}}' |
| Go |
go test -cover -coverprofile=coverage.out && go tool cover -html=coverage.out |
| Rust |
cargo tarpaulin --out Html --output-dir coverage |
See: references/coverage-strategies.md for branch coverage, mutation testing, and coverage-driven development.
Mocking Strategy
When to Mock
| Mock |
Don't Mock |
| Network calls (APIs, databases) |
Internal implementation details |
| Filesystem access |
Value objects and data structures |
| Time/randomness dependencies |
The code under test |
| Slow or unreliable dependencies |
Simple collaborators (prefer real objects) |
| Paid third-party APIs |
|
Mock Types
- Stub - Returns predefined values. Use for simple dependency replacement.
- Spy - Records calls for verification. Use when you need to assert interactions.
- Fake - Simplified working implementation (e.g., in-memory repository). Use for complex dependencies.
Cross-Language Mocking
| Language |
Tool |
Verify Call |
| Python |
unittest.mock.Mock() |
assert_called_once() |
| JavaScript |
jest.fn() |
expect().toHaveBeenCalled() |
| Go |
Interfaces + mock structs |
Track call state manually |
| Rust |
Traits + mock impls |
RefCell for interior mutability |
See: references/mocking-strategies.md for comprehensive patterns and anti-patterns.
Anti-Patterns
| Anti-Pattern |
Problem |
Fix |
| Testing implementation |
Brittle tests that break on refactor |
Test WHAT (outputs/behavior), not HOW (internal calls) |
| Flaky tests |
Non-deterministic failures |
Inject time deps, isolate state, use wait conditions for async |
| Over-mocking |
Tests verify mocks, not behavior |
Only mock external boundaries, use real objects internally |
Test Organization
- Structure: Separate
unit/, integration/, e2e/ directories
- Naming conventions:
test_*.py, *.test.js, *_test.go, tests.rs
- Function names: Descriptive --
test_user_login_with_invalid_password_returns_error() not test_case_1()
Quick Reference
| Task |
Python |
JavaScript |
Go |
Rust |
| Run tests |
pytest -x |
npm test -- --watch |
go test ./... |
cargo test |
| Coverage |
pytest --cov=. --cov-fail-under=80 |
jest --coverage |
go test -cover |
cargo tarpaulin |
Remember
- RED -> GREEN -> REFACTOR is mandatory
- Test behavior, not implementation
- 80% coverage minimum, critical paths 100%
- Mock external boundaries only
- Fast, isolated, deterministic tests
- One clear assertion per test (when practical)
- Arrange-Act-Assert for clarity
- Descriptive test names document behavior
- Fix flaky tests immediately (never ignore)
- Tests are first-class code (refactor them too)
Additional Resources
references/tdd-deep-dive.md - Advanced TDD techniques and when to break rules
references/mocking-strategies.md - Comprehensive mocking patterns and anti-patterns
references/test-patterns-by-language.md - Language-specific testing patterns
references/coverage-strategies.md - Advanced coverage techniques and mutation testing
1---2name: testing3description: Guides the user through test-first development and test strategy decisions. ALWAYS trigger on "write tests", "TDD", "test coverage", "mock", "test fails", "flaky test", "how to test", "unit test", "integration test", "e2e test", "test structure", "what to test", "test organization", "coverage report", "testing strategy", "arrange act assert". Use when writing new tests, choosing test types, setting up mocking, debugging flaky tests, improving coverage, or designing testable code. Different from qa-security agent which focuses on code review and security audits rather than test authoring.4---56# Testing Domain Skill78## TDD Cycle: RED -> GREEN -> REFACTOR9101. **RED** - Write a failing test. Must fail for the right reason.112. **GREEN** - Write minimum code to pass. No gold plating.123. **REFACTOR** - Improve design. Tests stay green.1314```python15# RED16def test_user_full_name():17 user = User(first="Jane", last="Doe")18 assert user.full_name() == "Jane Doe"1920# GREEN21class User:22 def __init__(self, first, last):23 self.first = first24 self.last = last25 def full_name(self):26 return f"{self.first} {self.last}"2728# REFACTOR: tests pass, clean up if needed29```3031**See:** `references/tdd-deep-dive.md` for advanced TDD techniques.3233## What to Test / What NOT to Test3435**DO test (behavior):**36- Public API contracts37- Edge cases and boundaries38- Error conditions39- State transitions40- Business logic4142**DON'T test (implementation):**43- Private methods (test through public API)44- Language features45- Third-party libraries (only verify integration)46- Trivial getters/setters47- Generated code, migrations, static config4849## Test Types5051### Unit Tests52- Run in milliseconds, no I/O, no external dependencies53- Deterministic, parallelizable54- Test single units of behavior5556### Integration Tests57- Seconds to run, may involve I/O58- Test real component boundaries59- Run sequentially if stateful6061### E2E Tests62- Slowest (seconds to minutes)63- Test complete user flows from user perspective64- Run against staging/test environment6566### Characterization Tests67- Capture existing behavior of legacy code before refactoring68- Document current behavior (even if wrong), then refactor against it6970**See:** `references/test-patterns-by-language.md` for language-specific frameworks and idioms.7172## Test Structure: Arrange-Act-Assert7374```python75def test_shopping_cart_total():76 # Arrange77 cart = ShoppingCart()78 cart.add_item(Item("Book", 10.00))7980 # Act81 total = cart.calculate_total()8283 # Assert84 assert total == 10.0085```8687Prefer one assertion per test. Exception: related assertions on the same object.8889## Coverage Requirements9091| Metric | Threshold |92|--------|-----------|93| Line coverage | 80% minimum (CI-enforced) |94| Branch coverage | More important than line coverage |95| Critical paths | 100% |9697### Coverage Commands9899| Language | Command |100|----------|---------|101| Python | `pytest --cov=myapp --cov-report=html --cov-fail-under=80` |102| JavaScript | `jest --coverage --coverageThreshold='{"global":{"lines":80}}'` |103| Go | `go test -cover -coverprofile=coverage.out && go tool cover -html=coverage.out` |104| Rust | `cargo tarpaulin --out Html --output-dir coverage` |105106**See:** `references/coverage-strategies.md` for branch coverage, mutation testing, and coverage-driven development.107108## Mocking Strategy109110### When to Mock111112| Mock | Don't Mock |113|------|-----------|114| Network calls (APIs, databases) | Internal implementation details |115| Filesystem access | Value objects and data structures |116| Time/randomness dependencies | The code under test |117| Slow or unreliable dependencies | Simple collaborators (prefer real objects) |118| Paid third-party APIs | |119120### Mock Types121122- **Stub** - Returns predefined values. Use for simple dependency replacement.123- **Spy** - Records calls for verification. Use when you need to assert interactions.124- **Fake** - Simplified working implementation (e.g., in-memory repository). Use for complex dependencies.125126### Cross-Language Mocking127128| Language | Tool | Verify Call |129|----------|------|-------------|130| Python | `unittest.mock.Mock()` | `assert_called_once()` |131| JavaScript | `jest.fn()` | `expect().toHaveBeenCalled()` |132| Go | Interfaces + mock structs | Track call state manually |133| Rust | Traits + mock impls | `RefCell` for interior mutability |134135**See:** `references/mocking-strategies.md` for comprehensive patterns and anti-patterns.136137## Anti-Patterns138139| Anti-Pattern | Problem | Fix |140|-------------|---------|-----|141| Testing implementation | Brittle tests that break on refactor | Test WHAT (outputs/behavior), not HOW (internal calls) |142| Flaky tests | Non-deterministic failures | Inject time deps, isolate state, use wait conditions for async |143| Over-mocking | Tests verify mocks, not behavior | Only mock external boundaries, use real objects internally |144145## Test Organization146147- **Structure:** Separate `unit/`, `integration/`, `e2e/` directories148- **Naming conventions:** `test_*.py`, `*.test.js`, `*_test.go`, `tests.rs`149- **Function names:** Descriptive -- `test_user_login_with_invalid_password_returns_error()` not `test_case_1()`150151## Quick Reference152153| Task | Python | JavaScript | Go | Rust |154|------|--------|------------|----|------|155| Run tests | `pytest -x` | `npm test -- --watch` | `go test ./...` | `cargo test` |156| Coverage | `pytest --cov=. --cov-fail-under=80` | `jest --coverage` | `go test -cover` | `cargo tarpaulin` |157158## Remember1591601. **RED -> GREEN -> REFACTOR** is mandatory1612. **Test behavior, not implementation**1623. **80% coverage minimum**, critical paths 100%1634. **Mock external boundaries only**1645. **Fast, isolated, deterministic tests**1656. **One clear assertion per test** (when practical)1667. **Arrange-Act-Assert** for clarity1678. **Descriptive test names** document behavior1689. **Fix flaky tests immediately** (never ignore)16910. **Tests are first-class code** (refactor them too)170171## Additional Resources172173- `references/tdd-deep-dive.md` - Advanced TDD techniques and when to break rules174- `references/mocking-strategies.md` - Comprehensive mocking patterns and anti-patterns175- `references/test-patterns-by-language.md` - Language-specific testing patterns176- `references/coverage-strategies.md` - Advanced coverage techniques and mutation testing177178<!-- Last reviewed: 2026-03 -->