What to test and what to skip
Before writing a Spring test, decide what kind to write — because Spring offers several, from a plain unit test to a full application boot, and using the wrong one is the most common testing mistake. A test that boots the whole context to check a method's arithmetic is slow and misdirected; a test that mocks so much it verifies nothing is worthless. This lesson is the strategy: the test types Spring gives you, and which to reach for when.
The testing pyramid, in Spring
The classic shape applies directly, and Spring's test types map onto it:
- Many fast unit tests — plain JUnit + Mockito, no Spring context. Test a service's or a helper's logic in isolation, mocking its dependencies. Milliseconds each. The base of the pyramid.
- Fewer slice tests — Spring boots part of the application:
@WebMvcTest(the web layer only),@DataJpaTest(the persistence layer only). Faster than a full boot, focused on one layer. - Fewer still integration tests —
@SpringBootTestboots the whole application context, optionally against a real database (Testcontainers). The most realistic, the slowest. The top of the pyramid.
The principle: push tests down the pyramid. Most of your tests should be fast unit tests of your own
logic; use slice tests for a layer's wiring; reserve full integration tests for the critical end-to-end
paths. A suite that is all @SpringBootTest is slow to run (so it runs rarely) and vague when it fails (the
whole app is involved); a suite of focused fast tests runs constantly and pinpoints breakage.
The four test types and when to use each
| Type | Boots | Use for |
|---|---|---|
| Unit (JUnit + Mockito) | nothing | Service/business logic in isolation, dependencies mocked |
@WebMvcTest |
the web layer (one controller) | Controller behaviour: routing, status, validation, serialisation |
@DataJpaTest |
the persistence layer | Repository queries, entity mapping, custom @Query |
@SpringBootTest |
the whole context | End-to-end: real wiring across layers, critical flows |
- Unit test — the default for logic. If a method computes, decides, or transforms, test it as a plain unit with its collaborators mocked. Fast, precise, no Spring.
@WebMvcTest— for the web layer: does the controller route correctly, return the right status, validate input, serialise the response? Mocks the service/repository below.@DataJpaTest— for the data layer: does this query return the right rows, does the mapping work? Runs against an in-memory (or real) database, rolling back each test.@SpringBootTest— for integration: the whole app wired together, for the handful of critical paths where "do all the layers actually work together" is the question.
Match the test to what you are verifying: logic → unit; web behaviour → @WebMvcTest; queries →
@DataJpaTest; the whole flow → @SpringBootTest. Using @SpringBootTest for everything is the
over-boot mistake; mocking the thing under test is the under-test mistake.
Test YOUR logic, not the framework
The same principle as every testing lesson in this course: test the code you wrote, not Spring. Do not
write a test that a @GetMapping maps a URL (Spring's job, exhaustively tested by Spring), or that
JpaRepository.save saves (Spring Data's job). Test your logic:
- Your service/business rules — a fee calculation, a status transition, an eligibility check (unit tests).
- Your custom queries — a derived method or
@Queryreturns the right rows (@DataJpaTest). - Your controller behaviour — your validation rejects bad input, your error handler returns the right
status, your endpoint requires auth (
@WebMvcTest). - Your critical flows — the whole "book a parcel" path works end to end (
@SpringBootTest).
A test that would only fail if Spring itself were broken is wasted effort and maintenance. The test that earns its keep fails when you introduce a bug in your code.
Prioritise failures and risky paths
With limited time, test in this order (as throughout the course):
- The failure and edge cases — invalid input returns 400, missing resource returns 404, unauthorised returns 401/403, a conflict returns 409. Highest value, most often skipped.
- The risky logic — anything touching money, state transitions, concurrency, security.
- The core happy paths — the main flows return the right result.
- Regression tests — when you fix a bug, write a test that reproduces it so it cannot return.
Notice what is not at the top: exhaustively testing trivial getters or framework behaviour. Aim for high-value coverage of your own risky logic, mostly with fast tests, and the suite stays fast, focused, and worth running on every change.
Check your work
The pyramid. Many fast unit tests (no Spring) → fewer slice tests (@WebMvcTest, @DataJpaTest, part of
the app) → few integration tests (@SpringBootTest, whole app). Push tests down; do not make everything
@SpringBootTest.
The four types and when. Unit+Mockito (logic, mocked deps); @WebMvcTest (web layer: routing/status/
validation/serialisation); @DataJpaTest (data layer: queries/mapping); @SpringBootTest (whole context,
critical end-to-end flows). Match the type to what you verify.
Test your logic, not the framework. Test services, custom queries, controller behaviour, critical flows
— not that Spring routes or that save saves. A test that only fails if Spring broke is wasted.
Priority. Failure/edge cases first, then risky logic (money/state/concurrency/security), then happy paths, then regression tests — high-value coverage, mostly fast tests.
Practice
- For five things in Dakiya, decide the test type (unit /
@WebMvcTest/@DataJpaTest/@SpringBootTest) and justify each. - Take a test written as
@SpringBootTestthat only checks a service method's logic and rewrite it as a fast unit test; note the speed difference. - Identify three tests that would only fail if Spring itself broke, and delete/avoid them.
- List Dakiya's risky paths (status transitions, auth, concurrency) and which test type covers each.
- Categorise ten hypothetical tests into the pyramid layers and check the shape is bottom-heavy.
- For a bug you fix, write the regression test first and confirm it fails before the fix and passes after.
Official documentation
- Spring Boot — Testing — The test types and annotations.
- Spring Boot — Test slices —
@WebMvcTest,@DataJpaTest, and the rest. - Martin Fowler — The Test Pyramid — The shape and why.
Next: unit testing services.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship