When you're building a web app with Spring — you need to make sure it actually works. For that there's a great tool called MockMvc. Think of your app as a modern cafe. Normally, to test how it works you'd have to open the whole place: kitchen, dining room, seat the waiters. That's like starting a full server — slow and heavy. MockMvc is like making a mini version of the cafe with one cook and a pickup window. You can make orders through the window (send requests) and get dishes back (responses) without running the whole business. In Spring terms that means you can test your app in a lightweight mode — no full server or DB connection required.
Setting up the test environment
First, make sure you've added the necessary test dependencies. In your pom.xml (if you're using Maven) or build.gradle (if Gradle) you should have the dependencies for Spring Boot Starter Test:
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Gradle:
testImplementation 'org.springframework.boot:spring-boot-starter-test'
MockMvc is already bundled with Spring Boot Starter Test, so you don't need extra dependencies.
Simple REST API testing with MockMvc
Input data
Let's say we have a REST controller for managing Product entities:
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
// This is a simplified stub. In real life there would be a call to a service.
if (id == 1L) {
return ResponseEntity.ok(new Product(1L, "Coffee", 5.0));
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
And we have the Product entity:
public class Product {
private Long id;
private String name;
private Double price;
// Constructors, getters and setters.
public Product(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
// Getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Writing tests
Now let's write tests for ProductController. We want to make sure that:
- If we request a resource with an existing ID, we get the correct result.
- If a resource with the given ID doesn't exist, the API returns a 404.
public class Product {
private Long id;
private String name;
private Double price;
// Constructors, getters and setters.
public Product(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
// Getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
- The
@WebMvcTestannotation: indicates we're testing only the controller layer. Spring will spin up the minimal required context. - The
mockMvc.perform()method: simulates an HTTP request. Here we specify the path (/api/products/1) and the method (GET). - The
andExpect()methods: help verify that the app returns the expected response. You can check:- Response status (
isOk(),isNotFound(), etc.). - Content type (
contentType()). - JSON response contents (
jsonPath()).
- Response status (
Testing POST requests
Now add a method to our controller to create a product:
@PostMapping
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
product.setId(2L); // Stub: always return the same ID
return ResponseEntity.status(HttpStatus.CREATED).body(product);
}
Let's write a test for this method. We want to make sure that:
- A POST request with valid data creates a product and returns status 201 (Created).
- The response body contains the created product.
@Test
public void testCreateProduct() throws Exception {
String newProductJson = """
{
"name": "Tea",
"price": 3.5
}
""";
mockMvc.perform(post("/api/products") // Emulate a POST request
.contentType(MediaType.APPLICATION_JSON) // Specify that the request body is JSON
.content(newProductJson)) // Send the JSON data
.andExpect(status().isCreated()) // Expect status 201 Created
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) // Response is JSON too
.andExpect(jsonPath("$.id").value(2)) // Check ID
.andExpect(jsonPath("$.name").value("Tea")) // Check product name
.andExpect(jsonPath("$.price").value(3.5)); // Check product price
}
Common mistakes and caveats
- Error "No qualifying bean of type '...' found"
When using@WebMvcTestonly controllers are started in the test context. If your controller depends on services or other components, don't forget to mock them with Mockito:@MockBean private ProductService productService; - Incorrect use of
@Autowired
Always use the right annotations (@Autowiredor@MockBean) for wiring dependencies. - JSON format in requests
Make sure the JSON data you send in tests is valid. UsecontentType(MediaType.APPLICATION_JSON). - Issues with
jsonPath
It's a good idea to use online JSONPath validators to ensure your path expressions are correct.
At this point you've learned how to test REST APIs with MockMvc. These skills will help you catch bugs early and give you confidence in interviews where you might be asked to demo working with MockMvc.
GO TO FULL VERSION