Should I test that?

Should I test every layer?

Verdict

No

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.

Why

Do not write separate tests for every layer. The typical case is a web backend with controller, service and repository layers, whose endpoint tests run each route against a real database in CI with a distinct value in each field. Blast radius is users and Change frequency is regularly, because most features touch all three layers. Detectability is immediately, because a broken layer fails the endpoint test before merge, so Reversibility is trivial. Test cost is moderate, because each layer test needs a mock of the layer below, and rule R8 gives Do not test; a layer whose rules or queries the endpoint tests do not tell apart gives Test.

When the decision changes
WhenDecisionWhy
The service layer holds branching rules, such as working-day due dates, and the endpoint tests run one branchTest: unit tests in the service layer for each branch and boundaryDetectability moves to eventually and Reversibility to with-effort, because a wrong saved date looks plausible
The repository layer has a hand-written query with filters, and the endpoint tests use rows that every filter lets throughTest: an integration test against a real database with rows that each filter must leave outDetectability moves to eventually and Reversibility to with-effort, because the query returns plausible wrong rows
A layer computes an invoice total or a refundTest mandatory: unit tests in that layer for each rule and the rounding boundariesBlast radius rises to money and Reversibility to costly
The repository layer adds the tenant ID to every query, and the endpoint tests use one tenantTest mandatory: a repository test with records of two tenants that checks each query returns one tenant's rowsBlast radius rises to safety-or-legal and Detectability to never, because a missing filter leaks records without an error
No test runs through all the layers, each layer is tested with the layer below mocked, and a broken seam fails with an error that users reportTest minimally: one endpoint test through all layers for each main pathDetectability moves to same-day
An assistant writes a mocked unit test for each layer in secondsDo not add the per-layer testsTest cost falls to trivial, but Detectability stays immediately and Reversibility trivial

What breaks if you don't test

Without per-layer tests, branches that no endpoint test takes go unchecked. An endpoint test that lists five shipments runs one path through the query's filters and the service's rules. A shipment on customs hold that the query drops passes the endpoint test, and the customer, who sees a plausible list, notices days later.

What you lose if you over-test

Mocked tests for every layer check one behaviour three times: adding a response field means editing three mocked tests plus the endpoint test. Each mocked test asserts the call its own author expected, so it stays green when two layers disagree about a page number. A repository test with a mocked database never runs the SQL, where repository bugs sit.

What to do instead

  1. Write one endpoint test for each main path and each error response, through all layers against a real database, with a distinct value in each field. Spring MockMvc runs it without a deployed server.
  2. Unit test rules in the layer that holds them, usually the service layer, one case per branch and boundary.
  3. Test hand-written queries against a real database, with rows that each filter must leave out.
  4. Add no test for a layer that only forwards calls. The Practical Test Pyramid splits unit and integration tests along the same lines.

When the answer changes

  • The endpoint tests run nightly instead of on each pull request, or do not exist.
  • A layer holds rules or queries with more cases than the endpoint tests run.
  • A layer computes money or filters records by tenant or owner.

Real incident + Code example

Page one that showed page two

On a logistics backend I worked on, each layer of the shipments list had unit tests with the layer below mocked. The controller passed ?page=1 to the service as 1. The service author assumed a zero-based number and passed 1 to Spring Data's PageRequest.of, which counts pages from zero. Both tests were green. In production, page one skipped the 20 newest shipments, and a customer with 12 shipments saw an empty list and wrote to support the next morning. We added one endpoint test with 25 shipments and deleted the two mocked tests:

// Controller test, service mocked: green
verify(shipmentService).list(customerId, 1, 20);

// Service test, repository mocked: green
verify(shipmentRepository)
    .findByCustomerIdOrderByCreatedAtDesc(customerId, PageRequest.of(1, 20));

// Endpoint test through all layers, real database: fails on the bug
@Test
void firstPageStartsWithNewestShipment() throws Exception {
    insertShipments(customerId, 25); // S-01 is the oldest, S-25 the newest
    mockMvc.perform(get("/customers/{id}/shipments?page=1", customerId))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.items.length()").value(20))
        .andExpect(jsonPath("$.items[0].reference").value("S-25"));
}

FAQ

Is it necessary to unit test every layer of an n-tier architecture?

No, a layer that only forwards calls needs no unit test when an endpoint test runs through it against a real database. Unit test the layers that hold rules.

Should you write integration tests at every level?

No, one integration test per main path through all layers covers the wiring between them. Add a narrower one for a repository query whose filters the endpoint tests do not tell apart.

Should I mock the layer below when I unit test a layer?

Replace the layer below only in a unit test of rules, such as a service that computes due dates, with an in-memory fake repository. A test that only verifies the call to the layer below stays green when the two layers disagree about an argument.

Which layer should I test in a layered architecture?

Test each behaviour in the layer that holds it: rules in the service layer, queries in the repository layer, and routes and serialization through an endpoint test. A layer with none of these needs only the endpoint test.