We've finally reached controller testing. Controllers, as you know, handle HTTP requests and pass data between the client and the app's business logic. Everyone wants their controllers to work flawlessly, but someone has to check that. Spoiler: that's gonna be you. Luckily, we have a powerful tool — MockMvc.
Why test controllers? Well, first, controller testing makes sure your endpoints work (and not only in your browser).
Second, controllers are often the first part of the app that users blame when something breaks. For example, what will a user do if a request to URI /api/users returns a 500 Internal Server Error? Of course they'll blame your code (and maybe you personally). So if your code works, you're covered.
What is MockMvc?
MockMvc is a tool provided by Spring Test that lets you test web app behavior without deploying a real server. It simulates handling HTTP requests and lets you assert responses, headers, statuses, and even errors.
MockMvc lets you send test HTTP requests to your controllers and check how they respond. For example, you can send a GET request to /api/users and verify that the response status is 200 and the body contains the expected data.
Here's a small example of interacting with MockMvc:
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(content().json("[{\"id\":1,\"name\":\"John Doe\"}]"));
Preparation for testing
Before you start writing tests, let's make sure we have everything we need:
- Controller: let's write a sample controller that we'll test.
- Dependencies: add Spring Boot Test, JUnit and Mockito for testing.
- MockMvc configuration: set up MockMvc for your tests.
Step 1: Example controller to test
Suppose we have a controller for working with users:
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}
The controller has two endpoints:
GET /api/users— returns a list of users.POST /api/users— adds a new user.
Step 2: Setting up dependencies in pom.xml
Add the required dependencies for testing:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
Step 3: Writing tests for the controller
Now let's create a test class for UserController.
Import the required classes
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
Set up the test class
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
// We'll write tests here
}
Step 4: Testing GET /api/users
Let's write a test to verify that GET /api/users returns the correct list of users.
@Test
public void shouldReturnListOfUsers() throws Exception {
// Create test data
List<User> users = List.of(new User(1L, "John Doe"), new User(2L, "Jane Doe"));
// Configure mock behavior
when(userService.findAll()).thenReturn(users);
// Send GET request and verify the result
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()").value(users.size()))
.andExpect(jsonPath("$[0].name").value("John Doe"))
.andExpect(jsonPath("$[1].name").value("Jane Doe"));
}
What's happening here:
- Create a list of users for the test.
- Configure the
userServicemock to return that list whenfindAll()is called. - Make a GET request to
/api/users. - Verify:
- Response status (200 OK).
- Size of the list in the JSON response.
- Value of the
namefield for the users.
Step 5: Testing POST /api/users
Test that POST /api/users creates a new user.
@Test
public void shouldCreateNewUser() throws Exception {
// Create a test user
User newUser = new User(null, "John Doe");
User savedUser = new User(1L, "John Doe");
// Configure mock behavior
when(userService.save(any(User.class))).thenReturn(savedUser);
// Send POST request and verify the result
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John Doe\"}"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
Here:
- We send a POST request with a JSON object.
- We check that:
- The response status is
201 Created - The response contains the ID and name of the new user.
- The response status is
Useful tips
- Don't forget about edge cases: test not only positive scenarios but also negative ones. For example, what happens if you send invalid data.
- Use handy methods: MockMvc provides lots of useful methods like
jsonPath(),header(), etc. - Clean up after yourself: make sure your tests don't rely on state left by previous tests. Each test should be independent.
What's next?
Now you know how to test REST controllers using MockMvc. This not only helps ensure quality but also looks great on your resume. In the next lecture we'll focus on testing services and repositories. For now, arm yourself with this knowledge and go test your controllers!
GO TO FULL VERSION