Testing Strategy & Testing Layers
This document defines the testing strategy for our projects. It is designed to be consumed by an AI agent or skill as well as by engineers.
Strategy Overview
Our strategy is based on the Testing Pyramid:
- Each layer has a unique responsibility, a specific level of isolation, and a clear objective.
- Lower layers are fast, isolated, and cost-effective; upper layers validate full integration, environment configuration, and user experience.
- Business logic MUST be tested at the lowest viable layer (build-time promotion) rather than deferred to higher layers.
- A testing layer SHOULD NOT duplicate the testing performed by a lower layer, although some overlap is acceptable.
/\
/ \ 6. Manual Test (Exploratory)
/----\
/ \ 5. E2E (End-to-End)
/--------\
/ \ 4. System Test
/------------\
/ \ 3. Contract Test
/----------------\
/ \ 2. Component Integration Test
/--------------------\
/ \ 1. Unit Tests
/------------------------\
Testing Layers
Tests may live in any of the following layers, applying the pyramid: promote testing to build time over tests that run after deployment.
1. Unit Test
- Purpose: Test the smallest piece of code that can be isolated - functions, methods, classes, or pure algorithms.
- Scope: If a test relies on or interact with external systems (databases, network, system configuration files, third-party APIs), it is NOT an unit test; everything external MUST be simulated or mocked in memory.
- Sources: Every business-logic file (
services,utils,helpers,reducers,useCases) must have an associated unit test file covering its logical branches.
2. Component Integration Test
- Purpose: Test that a whole component (service) integrates correctly with its internal infrastructure resources such as Databases, messaging brokers and cache stores.
- Scope: These tests treat the service as a black box and test it in isolation from the rest of the system; other services MUST be mocked, while infrastructure such as databases or message brokers MAY be exercised in-memory (e.g., Testcontainers).
- Front-end variant: For front-end products this layer also validates isolated UI components before they are integrated into the global application.
- Sources: Tests that exercise the component through its public interface (HTTP controllers, message listeners, routers) using embedded or local containerized infrastructure (e.g. in-memory databases, Testcontainers).
3. Contract Test
- Purpose: Validate that the communication interfaces (contracts) between services are not broken by changes. If a contract is violated, the tests must fail immediately.
- Scope: Contract Tests MUST exist for services that expose or consume REST APIs from other services, and for services that publish or consume events/messages via brokers.
- What it verifies: The shape and semantics of the interface itself — request/response structure for HTTP interactions, and message schema for event-driven interactions. It does not test business logic; it tests that both sides of a communication boundary agree on the interface.
- Sources: Test files using contract testing frameworks such as Pact and Spring Cloud Contract.
4. System Test
- Purpose: Test that a set of independent services (system) is correctly deployed and integrated into a specific environment.
- Scope: - System tests MUST focus on environment-specific failures — real connectivity to databases or brokers, incompatibilities between deployed services, and missing or incorrect environment configuration — not on logic already covered at lower layers.
- Source: Test scripts usually located in separated projects, not embedded in services source code. They are usually API tests implemented with Java testing frameworks.
5. E2E (End-to-End)
- Purpose: Exercise the system as a whole by automating business scenarios from a real user perspective (user journeys). They prevent alerts in production and validate the real interconnection of the Database, Backend, and FrontEnd working together seamlessly.
- Scope: E2E tests MUST cover only the real happy flows of critical business functionality; exhaustive combinations and deep functional bugs belong to the Unit and Component Integration layers.
- Sources: Suites that drive browsers or full customer flows (e.g., Playwright, Cypress, Selenium) against fully integrated environments.
6. Manual Test (Exploratory)
- Purpose: Tests executed by people using exploratory techniques based on learning, experience, and product knowledge.
- Objectives: Validate changes beyond what automation can see, cover experience-based edge cases, and regression-test scenarios not covered by automation.
- Timing: Manual exploratory testing SHOULD be performed as early as possible — ideally locally, before commits and pushes.