Testing Skill
Overview
Tests written after code pass immediately—proving nothing. This skill guides test-first development and systematic test quality review.
Core principle: Write the test FIRST. Watch it fail. Then write code to pass. If you didn't see it fail, you don't know it tests the right thing.
The Test-First Process
Phase 1: Write Failing Test
Before writing ANY implementation code:
Write One Minimal Test
- Tests ONE behavior
- Clear name describing expected outcome
- See
references/test-pyramid.mdfor structure
Watch It Fail
- Run the test
- Confirm it fails for the expected reason (missing feature, not typo)
- If test passes immediately → test is wrong, fix it
Verify Failure Message
- Does the error explain what's wrong?
- Would you understand this failure in 6 months?
Phase 2: Write Minimal Code
Only enough code to pass the test:
Implement Minimally
- Don't add features not tested
- Don't optimize
- Don't refactor yet
Watch Test Pass
- Run the test
- Confirm it passes
- If it fails → fix code, not test
Check Other Tests
- All existing tests still pass?
- If not → fix immediately before continuing
Phase 3: Refactor
Only after tests pass:
Clean Up Code
- Remove duplication
- Improve names
- Extract helpers
Keep Tests Green
- Run tests after every change
- If tests fail → undo refactor, try again
Don't Add Behavior
- Refactoring changes structure, not behavior
- New behavior requires new test first
Repeat
Write next failing test. Repeat cycle.
Red Flags - STOP and Fix
You're Doing It Wrong If:
- Writing code before tests
- Test passes immediately (you're testing existing behavior)
- Adding "just one more feature" without a test
- Debugging why test fails instead of why code is wrong
- Tests require running the whole system
- Can't describe what test is testing in plain English
Test Quality Red Flags:
- No assertions (test always passes)
- Assertions on mock behavior instead of real code
- Test logic (if/else in tests)
- Tests depend on each other (order matters)
- Flaky tests (pass sometimes)
- Slow tests (>100ms for unit tests)
Coverage Red Flags:
- High coverage, but bugs still found
- Tests only cover happy path
- Tests don't cover error handling
- Tests don't cover edge cases (null, empty, max values)
Common Rationalizations - Don't Accept These
| Excuse | Reality |
|---|---|
| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
| "I'll write tests after" | Tests after pass immediately. Proves nothing. |
| "I already manually tested" | Manual testing isn't repeatable. Automate it. |
| "Tests slow me down" | Debugging slows you down more. TDD is faster. |
| "It's hard to test" | Hard to test = hard to use. Improve the design. |
| "We're moving fast" | Fast now, slow later. Tests enable speed. |
| "I'll refactor later to make it testable" | Do it now. Later never comes. |
Test Quality Checklist
Before approving any code:
- Test First: Was the test written before the code?
- Failure Seen: Did developer watch test fail first?
- One Behavior: Does each test verify one thing?
- Clear Name: Does test name describe expected behavior?
- No Logic: Are tests simple (no if/else, loops)?
- Independent: Do tests pass in any order?
- Fast: Do unit tests run in <100ms each?
- Edge Cases: Are boundaries and errors covered?
Test Structure
The AAA Pattern
test('returns user when valid ID provided', async () => {
// Arrange - set up test data
const userId = 'user-123';
await createTestUser({ id: userId, name: 'Test' });
// Act - execute the code
const result = await getUser(userId);
// Assert - verify the outcome
expect(result.name).toBe('Test');
});
Naming Convention
Pattern: [unit] [behavior] when [condition]
Examples:
- "returns empty array when no users exist"
- "throws NotFoundError when user not found"
- "sends welcome email when user signs up"
Test Pyramid
/\
/ \ E2E (10%) - Critical user journeys only
/----\
/ \ Integration (20%) - API, DB, services
/--------\
/ \ Unit (70%) - Business logic, pure functions
/------------\
What to Test at Each Level
| Level | Test | Don't Test |
|---|---|---|
| Unit | Business logic, calculations, transformations | Frameworks, libraries, I/O |
| Integration | API endpoints, database queries, service calls | UI, external services |
| E2E | Critical user flows, auth, checkout | Everything (too slow) |
Quick Commands
# Run with coverage
npm test -- --coverage
pytest --cov=src
go test -cover ./...
# Run specific test
npm test -- -t "user"
pytest -k "user"
go test -run TestUser
# Watch mode
npm test -- --watch
pytest-watch
References
Detailed patterns and examples in references/:
test-pyramid.md- Test strategy and coverage guidelinesmocking-guide.md- When and how to mockflaky-tests.md- Diagnosing and fixing flaky tests
Converted and distributed by TomeVault — claim your Tome and manage your conversions.