Use when writing tests, structuring test suites, choosing test boundaries, or debugging test quality issues like flakiness, over-mocking, or brittle tests
View on GitHubSelect agents to install to:
npx add-skill https://github.com/lgbarn/shipyard/blob/main/skills/shipyard-testing/SKILL.md -a claude-code --skill shipyard-testingInstallation paths:
.claude/skills/shipyard-testing/<!-- TOKEN BUDGET: 400 lines / ~1200 tokens -->
# Writing Effective Tests
## Activation Triggers
- Writing or modifying test files (`*.test.*`, `*.spec.*`, `*_test.go`, `test_*.py`)
- Setting up test infrastructure or test utilities
- Debugging flaky, brittle, or slow tests
- Deciding between unit, integration, and E2E tests
- Choosing when and how to use mocks, stubs, or fakes
## Overview
Test behaviors through public APIs. Verify state, not interactions.
**Core principle:** If refactoring breaks your tests but not your users, your tests are wrong.
**Relationship to TDD:** The `shipyard:shipyard-tdd` skill covers WHEN to write tests (test-first, red-green-refactor). This skill covers HOW to write tests that are effective, maintainable, and trustworthy.
## The Iron Law
```
TEST BEHAVIORS, NOT IMPLEMENTATIONS
```
A test should break only when the system's observable behavior changes — never because of refactoring, renaming internals, or restructuring code.
**No exceptions:**
- Don't test private methods
- Don't assert on internal state
- Don't verify method call sequences
- Don't couple tests to data structures users never see
## Test Structure (AAA)
Every test follows Arrange-Act-Assert:
```
Arrange — Set up preconditions and inputs
Act — Execute the behavior under test
Assert — Verify the expected outcome
```
Separate the three sections with blank lines. One Act per test. One logical assertion per test.
<Good>
```python
def test_expired_subscription_denies_access():
# Arrange
user = create_user(subscription_end=yesterday())
# Act
result = check_access(user, resource="premium-content")
# Assert
assert result.denied is True
assert result.reason == "subscription expired"
```
Clear name, tests one behavior, obvious structure
</Good>
<Bad>
```python
def test_subscription():
user = create_user(subscription_end=yesterday())
assert check_access(user, "premium-content").denied
user.subscription_end = tomorrow()