Testing Strategy
Intro
Pick the cheapest test that gives you confidence the code is
correct. Many fast unit tests, fewer integration tests, and a thin
layer of end-to-end tests for the critical paths — that is the
testing pyramid, and it still works.
Overview
Match the test type to the code
- Pure logic (calculations, transformations) → unit tests.
- Integration points (database, HTTP API, file I/O) →
integration tests.
- User workflows (end-to-end paths) → E2E tests.
Apply the testing pyramid
- Many unit tests — fast, isolated, cheap to run on every commit.
- Fewer integration tests — slower, exercise the boundary between
your code and external systems.
- Minimal E2E tests — slowest and most flaky, reserved for the
critical happy paths a user must be able to complete.
Naming and shape
Name tests test_<function>_<scenario>_<expected_result> so
failures self-document. Each test follows the AAA pattern:
Arrange the inputs, Act by calling the unit, Assert
the expected outcome. One logical assertion per test where
practical.
Prioritize what you test
- Error paths and edge cases over happy paths
- Public API over internal implementation details
- Complex branching logic over trivial getters/setters
Coverage as a smoke alarm
Aim for ~80% coverage on business logic. Do not chase 100%
everywhere — coverage is a smoke alarm, not a goal. The last 20%
is usually trivial code, generated code, or defensive paths that
cost more to test than they save.
Example
User says: "I added a new calculate_discount() function, what
tests do I need?" The agent recommends:
test_calculate_discount_percentage_applied_correctly
test_calculate_discount_zero_amount_returns_zero
test_calculate_discount_negative_amount_returns_error
test_calculate_discount_exceeds_max_caps_at_limit
One happy path, one boundary, one error case, one limit case.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Testing implementation details (private methods, internal state). Tests that verify internals couple the test suite to the implementation — any structural change breaks tests even when behavior is correct. Test through the public API at the appropriate level.
- Slow unit tests that hit disk or network. A unit test that opens a file, makes an HTTP call, or connects to a database is an integration test with a misleading name. It will be slow and flaky in CI. Mock or stub at the boundary; keep unit tests pure.
- E2E tests for every code path. E2E tests are expensive to write, slow to run, and flaky to maintain. They should cover the critical user journeys that must work — not every conditional branch. Those belong in unit and integration tests.
- Coverage goals as the only quality metric. 100% line coverage with assertions that never fail (e.g.
assert True) provides zero confidence. Coverage is a smoke alarm — it tells you what was executed, not whether the behavior was verified.
- No test for the unhappy path. Error cases and edge cases are where bugs live. Happy-path-only tests give false confidence. For every behavior, consider: what happens with empty input, invalid input, a missing dependency, a limit exceeded?
- Deviating from the testing pyramid without understanding the trade-offs. If the test suite is inverted (mostly E2E) or trophy-shaped (mostly integration), that may be appropriate for the architecture — but it should be a deliberate decision with documented trade-offs, not an accident.
- Tests that pass when the production code is deleted. A test suite that stays green after the module is removed was not testing anything. Run a mutation test or delete the module temporarily to verify the suite would actually catch a regression.
Full reference
Choosing the right level
| Question |
Right test level |
| Does this pure function compute the right value? |
Unit |
| Does this query return the right rows from the DB? |
Integration |
| Does this HTTP endpoint persist and respond correctly? |
Integration |
| Can a user complete checkout end-to-end? |
E2E |
| Does this private helper handle edge case X? |
Test through the public API, not directly |
When to break the pyramid
- Inverted pyramid (lots of E2E): the system has weak
internal seams; integration tests would be more valuable but
the architecture makes them hard. Refactor for testability.
- Trophy shape (heavy integration): typical for thin services
where most logic is glue between external systems; the unit
layer is genuinely small.
- Diamond (heavy integration, light unit and E2E): common for
microservices that mostly orchestrate.
The pyramid is a default, not a law.
Anti-patterns
- Testing implementation details (e.g., asserting on private
method calls) — couples tests to refactoring
- Slow unit tests because they hit the disk or network
- E2E tests that exercise every code path instead of critical
flows
- Coverage goals as the only quality metric
- Tests that pass when the production code is deleted (they
weren't testing anything)
- Skipping tests for "trivial" code that turns out to be wrong
1---2name: testing-strategy3description: Testing approach — unit vs integration vs E2E, coverage goals, organization. Use when deciding what to test, choosing between unit/integration/E2E tests, planning a test strategy for new functionality, or setting coverage goals.4---56# Testing Strategy78## Intro910Pick the cheapest test that gives you confidence the code is11correct. Many fast unit tests, fewer integration tests, and a thin12layer of end-to-end tests for the critical paths — that is the13testing pyramid, and it still works.1415## Overview1617### Match the test type to the code1819- **Pure logic** (calculations, transformations) → unit tests.20- **Integration points** (database, HTTP API, file I/O) →21 integration tests.22- **User workflows** (end-to-end paths) → E2E tests.2324### Apply the testing pyramid2526- Many unit tests — fast, isolated, cheap to run on every commit.27- Fewer integration tests — slower, exercise the boundary between28 your code and external systems.29- Minimal E2E tests — slowest and most flaky, reserved for the30 critical happy paths a user must be able to complete.3132### Naming and shape3334Name tests `test_<function>_<scenario>_<expected_result>` so35failures self-document. Each test follows the AAA pattern:36**Arrange** the inputs, **Act** by calling the unit, **Assert**37the expected outcome. One logical assertion per test where38practical.3940### Prioritize what you test4142- Error paths and edge cases over happy paths43- Public API over internal implementation details44- Complex branching logic over trivial getters/setters4546### Coverage as a smoke alarm4748Aim for ~80% coverage on business logic. Do not chase 100%49everywhere — coverage is a smoke alarm, not a goal. The last 20%50is usually trivial code, generated code, or defensive paths that51cost more to test than they save.5253### Example5455User says: "I added a new `calculate_discount()` function, what56tests do I need?" The agent recommends:5758- `test_calculate_discount_percentage_applied_correctly`59- `test_calculate_discount_zero_amount_returns_zero`60- `test_calculate_discount_negative_amount_returns_error`61- `test_calculate_discount_exceeds_max_caps_at_limit`6263One happy path, one boundary, one error case, one limit case.6465## Gotchas6667Agent-specific failure modes — provider-neutral pause-and-self-check items:6869- **Testing implementation details (private methods, internal state).** Tests that verify internals couple the test suite to the implementation — any structural change breaks tests even when behavior is correct. Test through the public API at the appropriate level.70- **Slow unit tests that hit disk or network.** A unit test that opens a file, makes an HTTP call, or connects to a database is an integration test with a misleading name. It will be slow and flaky in CI. Mock or stub at the boundary; keep unit tests pure.71- **E2E tests for every code path.** E2E tests are expensive to write, slow to run, and flaky to maintain. They should cover the critical user journeys that must work — not every conditional branch. Those belong in unit and integration tests.72- **Coverage goals as the only quality metric.** 100% line coverage with assertions that never fail (e.g. `assert True`) provides zero confidence. Coverage is a smoke alarm — it tells you what was executed, not whether the behavior was verified.73- **No test for the unhappy path.** Error cases and edge cases are where bugs live. Happy-path-only tests give false confidence. For every behavior, consider: what happens with empty input, invalid input, a missing dependency, a limit exceeded?74- **Deviating from the testing pyramid without understanding the trade-offs.** If the test suite is inverted (mostly E2E) or trophy-shaped (mostly integration), that may be appropriate for the architecture — but it should be a deliberate decision with documented trade-offs, not an accident.75- **Tests that pass when the production code is deleted.** A test suite that stays green after the module is removed was not testing anything. Run a mutation test or delete the module temporarily to verify the suite would actually catch a regression.7677## Full reference7879### Choosing the right level8081| Question | Right test level |82|---|---|83| Does this pure function compute the right value? | Unit |84| Does this query return the right rows from the DB? | Integration |85| Does this HTTP endpoint persist and respond correctly? | Integration |86| Can a user complete checkout end-to-end? | E2E |87| Does this private helper handle edge case X? | Test through the public API, not directly |8889### When to break the pyramid9091- **Inverted pyramid (lots of E2E):** the system has weak92 internal seams; integration tests would be more valuable but93 the architecture makes them hard. Refactor for testability.94- **Trophy shape (heavy integration):** typical for thin services95 where most logic is glue between external systems; the unit96 layer is genuinely small.97- **Diamond (heavy integration, light unit and E2E):** common for98 microservices that mostly orchestrate.99100The pyramid is a default, not a law.101102### Anti-patterns103104- Testing implementation details (e.g., asserting on private105 method calls) — couples tests to refactoring106- Slow unit tests because they hit the disk or network107- E2E tests that exercise every code path instead of critical108 flows109- Coverage goals as the only quality metric110- Tests that pass when the production code is deleted (they111 weren't testing anything)112- Skipping tests for "trivial" code that turns out to be wrong