Test Writer
Decision Tree
Need to write a test → What kind?
├─ Testing a pure function → Unit test, no mocking needed
├─ Testing a class with dependencies → Unit test, mock dependencies
├─ Testing API endpoint → Integration test, real server + test DB
├─ Testing user workflow → E2E test (Playwright / platform integration test)
└─ Testing UI appearance → Snapshot test
What to Test
| Always Test | Skip |
|---|
| Business logic / core algorithms | Framework internals |
| Edge cases (empty, null, boundary) | Simple getters/setters |
| Error paths and error messages | Third-party library behavior |
| State transitions | Private methods (test via public API) |
| Integration points (API, DB) | UI layout pixel-perfection |
| Security-sensitive paths (auth, validation) | Generated code |
Naming Convention
test_[unit]_[scenario]_[expected result]
# Examples:
test_login_with_valid_credentials_returns_token
test_login_with_expired_password_returns_403
test_cart_add_item_when_full_raises_limit_error
Assertion Best Practices
- One logical assertion per test - Test one behavior, not one
assert statement
- Assert on behavior, not implementation - Check return values, not internal state
- Use specific assertions -
assertEqual(x, 5) not assertTrue(x == 5)
- Include failure messages -
assertEqual(result, 5, "discount should be 5% for VIP")
- Test exact error types -
assertRaises(ValueError) not assertRaises(Exception)
Mocking Strategy
| Mock | Don't Mock |
|---|
| External APIs / HTTP calls | The unit under test |
| Database (in unit tests) | Simple value objects |
| File system / network | Pure functions |
| Time / randomness | Internal collaborators (usually) |
| Third-party services | Everything (over-mocking = brittle tests) |
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|
| Testing implementation details | Breaks on refactor | Test behavior/output |
| Flaky tests ignored | Erode trust in test suite | Fix or delete immediately |
| Copy-paste test setup | Hard to maintain | Use fixtures/factories |
| No assertion (test just "runs") | Proves nothing | Always assert expected outcome |
| Testing too much in one test | Unclear what failed | One behavior per test |
| Mocking everything | Tests prove nothing | Only mock boundaries |
Test Structure (Arrange-Act-Assert)
# Arrange - Set up inputs and expected outputs
user = create_user(role="admin")
# Act - Call the function under test
result = permissions.can_delete(user, resource)
# Assert - Verify the result
assert result is True
Coverage Targets
- New code: 80%+ line coverage
- Critical paths (auth, payments, data): 95%+
- Don't chase 100%: Diminishing returns past 85% overall