Should I test that?

Should I unit test everything?

Verdict

No

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.

Why

Do not write a unit test for every function and class; unit test the code with logic and stakes, and let endpoint tests cover the glue. The typical case is what a test-everything rule adds: controllers, mappers, wiring and one-line delegations in a product web application with endpoint tests in CI. Blast radius is users and Change frequency is regularly. Detectability is immediately, because a mistake in glue code fails an endpoint test that puts a distinct value in each field. Reversibility is trivial, because reverting code that only passes data leaves nothing behind, and Test cost is moderate, because a unit test of glue needs a mock per collaborator and a rewrite per refactor. Rule R8 gives Do not test; if Detectability moves to same-day, the decision becomes Test minimally.

When the decision changes
WhenDecisionWhy
The code computes a price, a discount or an invoice totalTest mandatory: the main path, each discount rule and the rounding boundariesBlast radius rises to money and Reversibility to costly
The code decides who may read a record, such as a check of the user's organisation IDTest mandatory: one allowed and one denied case for each roleBlast radius rises to safety-or-legal and Detectability to never
The code picks which customers get an email, such as a reminder jobTest: cases with customers who must and must not receive the emailReversibility rises to impossible and Detectability to eventually: a sent email cannot be recalled, and a wrong recipient raises no error
A function holds branching rules, such as working-day due dates, and endpoint tests run one branchTest: each branch plus boundaries such as a Friday and a month endDetectability moves to eventually and Reversibility to with-effort, because a wrong saved date looks plausible
A mapper copies several fields of one type, and the endpoint tests put the same value in eachTest: one test with a distinct value in each fieldDetectability moves to eventually and Reversibility to with-effort, because a swapped field is saved
No endpoint or integration test runs the glue code, and a broken binding shows an error page that users reportTest minimally: one endpoint test for each main pathDetectability moves to same-day

What breaks if you don't test

A mistake in glue code under endpoint tests fails CI before the merge. The failures that reach users come from logic whose branches those tests never take: a function meant to add three working days passes a test that uses a Monday, but if it counts calendar days, a Thursday task gets a Sunday deadline. That gap needs a unit test of the date function, not of every class around it.

What you lose if you over-test

A unit test of a controller with three collaborators needs three mocks and asserts the calls the controller makes, so it repeats the controller's body and fails on refactors that change no response. With only such tests, the coverage report marks glue code as covered although no test checks what the endpoint returns.

What to do instead

  1. Write one endpoint or integration test for each main path, with a distinct value in each field, as the Spring MockMvc documentation shows for Java.
  2. Unit test the code that holds logic: branches, calculations, dates, money and access checks.
  3. Leave controllers, mappers whose fields differ in type, configuration and one-line delegations to the endpoint tests, and check once in the coverage report that those tests run them.

When the answer changes

  • The code computes money, decides access, or sends messages to customers.
  • The endpoints have no integration tests, or those tests reuse one value across fields.
  • Your team requires a unit test for every class: follow the rule, because a shared rule costs less than a debate per change.

Real incident + Code example

The rule that tested every class and missed the swap

On an order management API I worked on, the rule was one unit test class per production class. The suite reached about 1,900 mock-heavy unit tests. Moving the tax lookup from OrderService into a new TaxService turned 140 tests red without changing any response, costing two developers most of a week. That month, a mapper swapped billingAddress and shippingAddress when saving an order. Its unit test used one address for both fields, and the service tests mocked the mapper, so everything stayed green. For nine days, customers with two addresses got parcels at their billing address, until the warehouse asked why returns had doubled. We deleted about 600 mock-only tests and added endpoint tests like this:

@Test
void orderKeepsBillingAndShippingApart() throws Exception {
    mockMvc.perform(post("/orders")
            .contentType(MediaType.APPLICATION_JSON)
            .content("""
                {"billingAddress": {"city": "Lviv"},
                 "shippingAddress": {"city": "Odesa"}}"""))
        .andExpect(status().isCreated());

    Order saved = orders.findAll().get(0);
    assertEquals("Lviv", saved.billingAddress().city());
    assertEquals("Odesa", saved.shippingAddress().city());
}

FAQ

Should you write unit tests for everything?

No, write unit tests for code with logic and stakes, such as price calculations, access checks and branching rules. Controllers, mappers and wiring are covered by endpoint tests that give each field a distinct value.

Should you unit test every function?

No, a function needs its own unit test when it has branches, computes money, checks access, or copies several values of one type. A one-line delegation whose callers have tests in CI needs none.

Do I need to test everything?

No, you need a test for every behaviour whose failure would reach users unnoticed, not for every line. In a web application, that means an endpoint test per main path plus unit tests for the logic.

Should we test all our methods?

No, test the methods callers use through the public interface; the callers' tests cover getters, one-line delegations and private helpers. Add a direct test when a mistake would pass every caller test, such as a swap of two string fields.