Should I test that?

Should I test methods that only call another method?

Verdict

No

Do not write a unit test for a method that only forwards one value, or values of different types, to another method; test the code that calls it.

Why

Do not write a unit test for a method that only calls another method and returns the result; test the code that calls it. The typical case is a service method findById(id) that returns repository.findById(id), and controller or integration tests run it in CI. Blast radius is users, because a wrong result reaches a screen, and Change frequency is rarely, because a pass-through changes when its delegate is renamed. Detectability is immediately and Reversibility is trivial: a wrong call fails to compile or fails the callers' tests before merge, and a revert leaves no data behind. Test cost is trivial, and rule R8 gives Do not test.

When the decision changes
WhenDecisionWhy
The pass-through forwards several arguments of one type, such as a start date and an end dateTest: one test that passes a distinct value for each argument and checks where each one landsDetectability moves to eventually: a swapped argument produces a plausible result of the right type
The delegate has a sibling with the same signature, such as `findAll()` next to `findAllByActiveTrue()`Test: one test with data that only the right method leaves outDetectability moves to eventually and Reversibility to with-effort: the wrong call returns plausible rows that code acts on, and callers' tests pass when their data cannot tell the two methods apart
No test runs the code that calls the pass-throughTest minimally: one test of the main path, on the caller or through the pass-throughDetectability moves to same-day: the first report of a wrong result comes from a user
The pass-through forwards an amount and a currency to a payment clientTest mandatory: assert the amount and the currency that a fake payment client receivesBlast radius rises to money and Reversibility to costly, because a wrong charge ends in refunds
The pass-through carries an access annotation, such as Spring's `@PreAuthorize`Test mandatory: one allowed call and one denied call through the security contextBlast radius rises to safety-or-legal and Detectability to never: without the annotation, every user can call the method and nothing fails
The pass-through deletes records, such as `close(id)` that calls `repository.deleteById(id)`, and no caller test checks that the other records remainTest: one test against a test database that deletes one record and checks that the others remainDetectability moves to eventually and Reversibility rises to costly: a caller test with one stored record passes when every record is deleted, and deleted records come back only from a backup, by hand

What breaks if you don't test

When the callers' tests run the pass-through, nothing breaks that CI misses. The failures that get through are calls that compile and return a plausible result: findBetween(start, end) that calls findByDateBetween(end, start), or findActive() that calls findAll() and lists closed accounts. A caller test catches them only when its data tells the two cases apart; otherwise a support agent reports the wrong list days later.

What you lose if you over-test

The usual pass-through test mocks the delegate and checks verify(repository).findById(42). That test repeats the method body: it fails only on an intended change, and a test written from a wrong body copies the mistake. In a service with 30 pass-throughs, one repository rename becomes 30 test edits, and no test checks which rows the database returns.

What to do instead

Test the behaviour one level up: an HTTP-level test of the endpoint, or an integration test of the use case, runs the pass-through with the code that decides the result. Give the delegate its own tests where it holds logic, such as a custom query, against a test database; the Spring Boot testing reference covers web and database test slices. Test an access annotation on a pass-through with the Spring Security method test support.

When the answer changes

  • The pass-through forwards two or more arguments of one type, such as two account IDs.
  • The delegate has a sibling with the same signature, such as findAll() and findAllByActiveTrue().
  • An annotation on the pass-through adds an access check, a transaction, or a cache.

Code example + Counterexample

The pass-through that listed closed accounts

Skipping fails when the delegate has a sibling with the same signature. The compiler accepts either call, a mock test copies whichever call the body makes, and only a test whose data tells the two apart fails:

List<Customer> findActive() {
    return customers.findAll(); // bug: should call findAllByActiveTrue()
}

// Passes: the mock repeats the line it checks
when(customers.findAll()).thenReturn(List.of(ana));
assertEquals(List.of(ana), service.findActive());

// Fails: the test database holds a closed account
@Test
void findActiveLeavesOutClosedAccounts() {
    customers.save(new Customer("ana", true));
    customers.save(new Customer("bo", false));
    assertEquals(List.of("ana"), names(service.findActive()));
}

A closed account in a customer list looks like ordinary data, so Detectability moves to eventually. A job that updates the listed customers also changes closed accounts, which someone fixes by hand, so Reversibility moves to with-effort and the framework gives Test for findActive().

FAQ

Do I need to write a unit test for a method that only calls a repository method?

No, a service method that only forwards to a repository method needs no unit test when a controller or integration test runs it. Test the repository method itself when it holds a custom query, against a test database.

Do I need to write unit tests for methods of services without logic?

No, the tests of the calling code cover a service method without logic. Add a test when the method forwards several arguments of one type, or when its delegate has a sibling with the same signature.

Should I use Mockito verify to test a method that only delegates?

No, a verify() check on a pass-through repeats the method body, so a test written from a wrong body passes. Use a test whose data the wrong delegate would handle differently, the state verification that Martin Fowler's article on mocks and stubs describes.