Testing Principles
Overview
Our overall testing principles can be nicely summarized by this article: Write tests. Not too many. Mostly integration by Ken Dodds.
Principles
Write Tests
- Automated tests save time by catching bugs early, avoiding issues during critical times.
- We aim to implement a shift-left approach in our testing process to identify and address bugs as early as possible in the workflow.
- Tests complement static typing and linting tools (SonarLint, Typescript, ESLint etc) to ensure business logic is bug-free.
Not Too Many
- Overemphasizing high test coverage (e.g., 100%) can lead to wasted effort and maintenance overhead.
- Although we aim for a 80% in coverage, the aim isn't to chase the coverage but have a strong foundation of tests.
- Avoid testing trivial implementation details, as this provides little confidence and complicates refactoring.
Mostly Integration
- Integration tests offer a balance between confidence and speed/cost, verifying components work together.
- Over-reliance on mocking reduces test reliability; minimize mocks to improve confidence in interactions.
- Testing individual units is helpful but insufficient without ensuring overall functionality.
- Too often you end up testing mocks rather than testing code
We aim to move beyond the traditional "Testing Pyramid" toward the "Testing Trophy" model, emphasizing integration over unit tests. Integration and end-to-end (E2E) tests yield higher confidence despite being slower or more resource-intensive.

Choosing the Right Testing Approach
When deciding which type of test to write, consider the following guidelines:
Integration Tests (default choice)
- Use this approach when you're testing the core business logic or interactions between multiple components.
- If your system has complex combinations or integrations (e.g., many interdependent modules or services), integration tests will ensure that these pieces work together as expected.
Use When
- Testing workflows, critical paths, business logic, or when multiple components need to be verified for correct interactions.
Unit Tests
- Use unit tests when you need to test very specific functionality, edge cases, or low-level logic in isolation.
- If your function is handling complicated conditions, edge cases, or error handling, unit tests can verify that individual components handle these cases correctly.
Use When
- Focusing on small, isolated pieces of functionality, such as handling an edge case or test cases with a high quantity of combinatorial conditions.
End-to-End (E2E) Tests
- E2E tests are best suited for verifying the full user journey or high-level workflows, especially for happy path testing.
- These tests ensure that the system behaves as expected from the user's perspective, across the full stack, including front-end, back-end, and everything in between.
Use When
- Verifying the system’s end-to-end functionality, such as a smoke test to check that everything is working as expected in the production environment.
To summarize
We're primary focus is on static testing and integration testing. While unit testing and end-to-end (E2E) testing are included in our strategy, the majority of our efforts are dedicated to the first two.
Further Reading
For more in-depth perspectives on testing strategies and models, explore the following resources: