Test depth
How far to go: coverage targets, E2E for every page, regression tests, snapshots and mutation testing.
Most asked
Yes15
Yes, end-to-end tests are worth it for the main user flows: write one browser test per flow, run them on every pull request, and test rules and edge cases with unit tests.
Snapshot tests are worth it in small numbers: keep one short snapshot per UI component for the text users read, review every snapshot diff like code, and check behaviour with explicit assertions.
Yes, you need unit tests next to E2E tests for every rule with branches, one test per branch and boundary, while the E2E tests keep covering the main user flows.
Yes, 80% code coverage is enough as a number for a product codebase; stop writing tests to raise it, give each feature that no test runs one test of its main path, and fully test uncovered code that moves money or checks access.
Yes, contract testing is worth it between services that different teams deploy on their own schedules: write a Pact consumer test for each request the consumer makes, listing only the fields it reads, and let the provider verify those contracts in CI before each deploy.
- Is it enough to test only the happy path?
Yes, test the unhappy paths too: in typical product code the happy path alone is not enough, so add tests for the failure branches most likely to break, such as a failed call to another service or a partial save.
Code under test: Yes
Yes, mutation testing is worth it on the code each pull request changes: run StrykerJS or PIT on those files in CI and add an assertion for every surviving mutant that shows a real gap; do not run it over the whole codebase or set a score target.
Yes, property-based testing is worth it for functions with a wide input space and a rule you can state, such as a parser or a serializer: add one or two properties per function next to the example tests and run them in CI; do not write properties for glue code.
Yes, measure branch coverage on every pull request in CI and read the changed lines that no test runs; do not set a percentage goal for the whole codebase.
Yes, test error handling: write one test for each distinct thing your code does after a failure, such as a retry, a fallback or an error response, and assert what the caller gets and what the handler leaves behind.
Yes, test each kind of invalid input that your code promises to reject, with one value per kind and an assertion on the error type; do not test values that the parameter types already rule out.
Yes, a typical bug fix gets one regression test that fails on the unfixed code and passes with the fix, written at the lowest level that reproduces the bug and run in CI.
Yes, write an automated acceptance test for each acceptance criterion of a user story: run it through the API with seeded data instead of the browser, and keep it in CI after the story is closed.
Yes, write integration tests for the code that talks to your database or to another service: one test per main path against a real database in CI, and one test per hand-written query with rows that each filter must leave out.
- Should integration tests use mocks?
Yes, integration tests should use mocks only for services you do not run: replace each third-party API with a fake server that returns recorded responses, including errors, and run your database and your own code for real.
Code under test: Yes
No7
No, do not add unit tests for code whose results your integration tests already assert in CI; add unit tests only for logic with more input combinations than those tests run, such as date rules, and for code that computes money or decides access.
No, do not aim for 100% test coverage in a product codebase; test the code where a failure costs money, access or stored data, and read the coverage report for untested lines of that kind.
No, do not test every value a function accepts; test one value from each class of input that the code treats in its own way, plus the values on both sides of every boundary.
No, do not add a test for code whose results the tests of its callers already assert in CI, with data that gives each branch a different result; add a direct test only for a branch or a result that those tests never check.
Do not write tests for an input that no caller can produce; make the code reject that input loudly through a type or a guard that throws, and test the check that rules the input out.
No, do not give every layer its own tests; test each rule once in the layer that holds it, and let one endpoint test per main path run through all layers against a real database.
Do not write a unit test for every function and class; unit test the code with logic and stakes, and let endpoint tests that put a distinct value in each field cover controllers, mappers and wiring.
Test it differently1
- Should I write performance tests?Test it differently
Watch performance in production instead of load testing every change: record the latency of each endpoint and alert when its 95th percentile rises after a release.