Start Learning
Javaneer
Back to stage
Module 5·Testing Spring Applications

The Spring Test Stack

What spring-boot-starter-test gives you, and when to load the whole context with @SpringBootTest versus a thin slice.

14 min readIntermediate
On this page

You've built a real application - a secured, database-backed API. How do you know it works, and stays working as you change it? Tests. Spring Boot ships a rich testing toolkit, and the key skill is choosing the right kind of test for each job.

What you already have

Every Boot project includes spring-boot-starter-test, which bundles the essentials:

  • JUnit 5 — the test framework (@Test, @BeforeEach).
  • AssertJ — fluent assertions (assertThat(x).isEqualTo(y)).
  • Mockito — creating mock objects.
  • Spring's test support — booting contexts and test slices.

You've already used the first three for plain unit tests. Now you'll add Spring's part.

The spectrum: unit to full context

Not every test should start Spring. There's a spectrum, from "no framework at all" to "the whole application," trading speed for realism:

What each kind of test loadsPick a test type and see which layers of the app it spins up - and how that trades realism for speed.
Controller / Web layer
MockMvc, JSON, routing
loaded
Service layer
your business logic
not loaded
Repository layer
Spring Data / JPA
not loaded
Database
a real or embedded DB
not loaded
Speed
fast

Loads only the web layer + MockMvc. Services/repositories are mocked (@MockitoBean). Tests routing, validation, JSON, status codes.

  • Plain unit test — no Spring; new the class and pass mocks. Milliseconds. Best for pure logic (BookVault's BookService rules).
  • Slice tests (@WebMvcTest, @DataJpaTest) — start just one layer. Fast, and focused on that layer.
  • @SpringBootTest — start the entire application context. The most realistic and the slowest; reserve it for end-to-end checks.
Test at the right zoom level

A mechanic doesn't run the whole car on a rolling road to check a single spark plug - they test the plug on the bench. But to verify the car actually drives, they do take it out on the road. Tests are the same: bench-test a unit in isolation when you can (fast, precise), and only run the whole application when you specifically need to prove the pieces work together. Matching the test's scope to the question keeps your suite fast.

The golden rule

Prefer the smallest test that answers your question. Testing a validation rule? A slice or unit test. Verifying a SQL query? @DataJpaTest. Confirming security rules across the real stack? @SpringBootTest. Reaching for @SpringBootTest for everything gives you a slow suite that developers stop running - the opposite of helpful.

Choose the test

For each, pick the lightest test type that fits: plain unit, @WebMvcTest, @DataJpaTest, or @SpringBootTest.

  1. BookService rejects a duplicate ISBN.
  2. GET /api/books/{isbn} returns 404 for an unknown ISBN.
  3. BookRepository.findByAuthorIgnoreCase(...) returns the right rows.
  4. A LIBRARIAN token can POST a book but a MEMBER token gets 403 (end to end).

What is the trade-off @SpringBootTest makes compared with a slice or unit test?

Key takeaways

  • spring-boot-starter-test bundles JUnit 5, AssertJ, Mockito, and Spring's test support.
  • Tests span a spectrum from plain unit (no Spring) through slices (@WebMvcTest, @DataJpaTest) to @SpringBootTest (full context), trading speed for realism.
  • Prefer the smallest test that answers your question; reserve @SpringBootTest for end-to-end checks.
  • An over-reliance on @SpringBootTest makes the suite slow, so developers run it less - defeating the point.
  • Match the test's scope to the question, like choosing the right zoom level.
Was this lesson helpful?
Edit this page on GitHub