Testing with @SpringBootTest & MockMvc
Spring Boot makes tests first-class: @WebMvcTest slices load just your controllers, MockMvc fires simulated HTTP requests, @MockBean stubs collaborators, and @SpringBootTest wires the whole app for integration checks.
Learn Testing with @SpringBootTest & MockMvc in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Before You Start
You should understand dependency injection (why mocking works) and the basics of REST controllers in Spring. Comfort with interfaces helps for mocking.
What You'll Learn
The Big Idea — Test the Engine on a Bench, Not Just the Whole Car
💡 Analogy: A car maker doesn't crash-test a whole car for every tiny change. They put the engine on a test bench (a slice ), feed it controlled inputs (a mock fuel supply), and measure the output — fast and focused. @WebMvcTest + MockMvc is that bench for your controllers: it spins up just the web layer and lets you fire fake requests at it, with @MockBean standing in for the rest of the engine. Occasionally you still do a full road test ( @SpringBootTest ) to confirm everything works together — but you don't do it for every screw you turn.
Small, fast slice tests for most cases; a few full integration tests for the whole-car confidence.
1️⃣ The Foundation — Arrange, Act, Assert
Every test, Spring or not, follows the same shape: arrange the inputs, act by calling the code, and assert the result. JUnit 5 gives you @Test and assertEquals ; here's the mindset in plain Java.
2️⃣ Isolating with Mocks — the @MockBean Idea
To test one component without its real dependencies, you replace a collaborator with a mock you control. In Spring that's @MockBean ; in plain Java we just inject a stub implementation.
3️⃣ Slice Tests — @WebMvcTest + MockMvc
@WebMvcTest loads only the web layer. MockMvc sends simulated HTTP requests, and @MockBean provides the controller's service. You assert status and JSON with fluent matchers.
4️⃣ Full Integration — @SpringBootTest
When you need real beans wired together, @SpringBootTest boots the whole context. It's slower, so reserve it for end-to-end confidence rather than every test.
🧠 Quick Recall
Answer: @SpringBootTest loads the full context; @WebMvcTest loads only the web-layer slice.
Answer: a bean in the test context with a Mockito mock you can stub and verify.
Answer: no — it dispatches simulated requests through the mock servlet environment, no port opened.
🎯 YOUR TURN — Write Two Assertions
Fill in two check(...) calls so both pass — the arrange-act-assert rhythm.
🧩 MINI-CHALLENGE — Stub and Verify
Build a recording mock for a NotificationGateway , inject it, then verify the interactions.
Common Errors
- ❌ Using @SpringBootTest for everything. Slow suites discourage testing. Fix: prefer slices like @WebMvcTest .
- ❌ Forgetting @MockBean in a slice. The context fails to start without the controller's service. Fix: mock missing collaborators.
- ❌ Mixing JUnit 4 and 5 annotations. org.junit.Test won't run under Jupiter. Fix: import from org.junit.jupiter.api .
- ❌ Not stubbing the mock. An un-stubbed Mockito mock returns null/0. Fix: set when(...).thenReturn(...) .
- ❌ Asserting only status, not body. You miss serialization bugs. Fix: add jsonPath checks.
- ❌ Sharing mutable state between tests. Causes flaky order-dependent failures. Fix: reset state in @BeforeEach .
Pro Tips
- 💡 Keep the test pyramid: many unit/slice tests, fewer full integration tests.
- 💡 Use AssertJ's assertThat for readable, fluent assertions.
- 💡 Name tests for behaviour (e.g. missingUserReturns404 ) so failures read like a spec.
- 💡 Verify interactions with Mockito.verify(...) when the side effect matters, not just the return value.
📋 Quick Reference
Concept
Syntax / idea
Note
test engine
JUnit 5 @Test
org.junit.jupiter.api
full context
@SpringBootTest
Integration, slower
web slice
@WebMvcTest(X.class)
Controllers only
drive requests
mockMvc.perform(get(...))
No real server
mock a bean
@MockBean
Mockito mock
stub
when(...).thenReturn(...)
Mockito
status assert
andExpect(status().isOk())
HTTP 200
body assert
jsonPath("$.name").value(...)
JSON field
setup
@BeforeEach
Per test
verify call
Mockito.verify(mock)
Interaction check
📚 Resources / Keep Learning
- 📘 Testing — Spring Boot reference
- 📗 JUnit 5 User Guide
- 📗 Testing the Web Layer — Spring guide
❓ Frequently Asked Questions
🎉 Lesson Complete!
You can now write arrange-act-assert tests in JUnit 5, slice the web layer with @WebMvcTest , drive controllers via MockMvc , stub collaborators with @MockBean , and boot the full app with @SpringBootTest when you need it.
Next up: Testing the Data Layer — @DataJpaTest , test slices, H2, and Testcontainers.
Practice quiz
What does @SpringBootTest do?
- Loads the full Spring application context for integration testing
- Runs only static analysis
- Compiles the code faster
- Deletes the database
Answer: Loads the full Spring application context for integration testing. @SpringBootTest bootstraps the complete ApplicationContext so you can test wired-together beans end to end.
What is @WebMvcTest used for?
- Loading the entire context
- Testing only the web layer (controllers) with a sliced context
- Testing the database only
- Running JavaScript
Answer: Testing only the web layer (controllers) with a sliced context. @WebMvcTest is a test slice that loads just the MVC components (controllers, filters) and not services or repositories.
What is MockMvc?
- A mock database driver
- A JSON library
- A way to perform HTTP requests against controllers without a running server
- A logging framework
Answer: A way to perform HTTP requests against controllers without a running server. MockMvc lets you send simulated HTTP requests to your controllers and assert on the response, with no real servlet container.
What does @MockBean do in a Spring test?
- Starts a real bean
- Deletes a bean permanently
- Encrypts a bean
- Replaces a bean in the context with a Mockito mock
Answer: Replaces a bean in the context with a Mockito mock. @MockBean adds or replaces a bean in the test ApplicationContext with a Mockito mock you can stub and verify.
Which testing framework does Spring Boot use by default?
- JUnit 3
- TestNG only
- JUnit 5 (Jupiter)
- Selenium
Answer: JUnit 5 (Jupiter). Spring Boot's spring-boot-starter-test brings JUnit 5 (Jupiter) as the default test engine.
Which annotation marks a JUnit 5 test method?
- @TestMethod
- @Test (org.junit.jupiter.api.Test)
- @RunTest
- @Spec
Answer: @Test (org.junit.jupiter.api.Test). In JUnit 5 a test is a method annotated with @Test from org.junit.jupiter.api.
Why prefer @WebMvcTest over @SpringBootTest for a controller unit test?
- It loads a smaller, faster slice of the context
- It tests the database too
- It encrypts requests
- It is required by Java 21
Answer: It loads a smaller, faster slice of the context. Loading only the web layer makes the test start faster and stay focused on controller behaviour.
With MockMvc, how do you assert the HTTP status is 200 OK?
- status().isError()
- expect(200)
- andExpect(status().isOk())
- assertHttp(ok)
Answer: andExpect(status().isOk()). perform(...).andExpect(status().isOk()) checks the simulated response returned a 200 status.
When you use @WebMvcTest, what happens to the controller's service dependency?
- You provide it as a @MockBean since services aren't in the slice
- It is auto-instantiated for real
- It is ignored
- It must be static
Answer: You provide it as a @MockBean since services aren't in the slice. The web slice doesn't load services, so you supply collaborators as @MockBean and stub their behaviour.
What does andExpect(jsonPath("$.name").value("Ada")) verify?
- The request header name
- The database column type
- That the JSON response field 'name' equals 'Ada'
- The server port
Answer: That the JSON response field 'name' equals 'Ada'. jsonPath navigates the JSON response body; here it asserts the 'name' field equals 'Ada'.
Continue this course
- Previous: JWT Authentication
- Next: Testing the Data Layer