Imagine we're building a microservice responsible for processing orders in an online store. It handles HTTP requests, calls services to check product availability in the warehouse, interacts with the database to persist order info. All of this is a set of interacting components. And even if you've tested them individually with unit tests, that doesn't guarantee everything works together correctly. That's exactly where integration testing comes into play.
Integration testing at the microservice level lets you:
- Verify the interaction between controllers, services, repositories and other components.
- Make sure the whole microservice functions correctly without depending on real external dependencies.
- Emulate real HTTP requests and check how the app handles them and returns responses.
MockMvc: the key tool for integration testing
Before we dive into testing, let's break down what MockMvc is.
MockMvc is a handy tool from Spring Test that lets you:
- Mock HTTP requests to your application without starting a real web server.
- Check how your controller handles requests, returns responses, and works with internal services.
- Simulate real API scenarios, including sending parameters, request bodies, custom headers, and more.
Think about it like this: instead of assembling the whole car and driving it on a test track, you test just the engine, but under load close to real conditions. In simple terms: MockMvc lets you test the web layer of your application in isolation, without the need to spin up the entire server.
Why MockMvc is better for integration tests?
MockMvc gives you an easy way to test:
- Faster than running full integration tests that start the server. This saves time, especially in big projects.
- Isolates the layer under test. If the database or other external systems aren't ready, tests won't be blocked because you can mock them.
- Flexible API. MockMvc makes it easy to send requests and verify how your controllers handle them.
Getting ready to work with MockMvc
Before we jump into examples, make sure your project is set up for testing.
First let's make sure your pom.xml (or build.gradle) includes the test dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
To work with MockMvc you'll need the following setup in your test class:
@SpringBootTest // Starts the Spring context for tests
@AutoConfigureMockMvc // Enables automatic MockMvc configuration
Easy, right? Now your test class is ready to go.
Practice: Testing a REST API with MockMvc
Enough theory — let's show how this works in practice!
Assume we have a microservice for managing books, with a REST API that works with a collection of books.
Controller for testing
Here's an example controller that provides HTTP methods for working with books:
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("/{id}")
public ResponseEntity<Book> getBook(@PathVariable Long id) {
return ResponseEntity.ok(bookService.findBookById(id));
}
@PostMapping
public ResponseEntity<Book> createBook(@RequestBody Book book) {
return ResponseEntity.status(HttpStatus.CREATED).body(bookService.saveBook(book));
}
}
We have two endpoints:
GET /books/{id}— returns a book by its identifier.POST /books— adds a new book.
Writing controller tests with MockMvc
Now let's write tests for this controller. First, create a test class:
@SpringBootTest
@AutoConfigureMockMvc
class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookService bookService; // Mocking the service dependency
@Test
void testGetBook() throws Exception {
// Test data
Book book = new Book(1L, "The Hobbit", "J.R.R. Tolkien");
Mockito.when(bookService.findBookById(1L)).thenReturn(book);
// Send GET request and verify the result
mockMvc.perform(get("/books/1"))
.andExpect(status().isOk()) // Expect 200 OK status
.andExpect(jsonPath("$.title").value("The Hobbit")) // Check the title field
.andExpect(jsonPath("$.author").value("J.R.R. Tolkien")); // Check the author field
}
@Test
void testCreateBook() throws Exception {
// Test data
Book book = new Book(null, "1984", "George Orwell");
Book savedBook = new Book(2L, "1984", "George Orwell");
Mockito.when(bookService.saveBook(Mockito.any())).thenReturn(savedBook);
// Send POST request with JSON payload
String bookJson = """
{
"title": "1984",
"author": "George Orwell"
}
""";
mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON)
.content(bookJson))
.andExpect(status().isCreated()) // Expect 201 Created status
.andExpect(jsonPath("$.id").value(2L)) // Verify that book ID = 2
.andExpect(jsonPath("$.title").value("1984")) // Verify title
.andExpect(jsonPath("$.author").value("George Orwell")); // Verify author
}
}
What's happening here?
- Annotations:
@SpringBootTestbrings up the full Spring context.@AutoConfigureMockMvcconfigures MockMvc automatically.@MockBeancreates a mock for theBookServicedependency that we use to isolate the controller.
- The tests:
- In
testGetBookwe mock thefindBookByIdmethod in the service to return aBookobject. Then we send a GET request and verify the response contains the correct data. - In
testCreateBookwe mock thesaveBookmethod, send a POST request with JSON payload and verify the response contains the created book data.
- In
- MockMvc API:
mockMvc.perform(...)executes the HTTP request.andExpect(...)holds all assertions on the result, from the response status to the JSON content.
Common mistakes when using MockMvc
So, what should you watch out for:
- MockMvc or
@AutoConfigureMockMvcnot declared. In that case tests won't be able to use MockMvc. - JSON serialization errors. Check if Jackson is included in your project.
- Missing mocks for dependencies. MockMvc tests only the controller layer, so be sure to mock services via
@MockBean.
MockMvc is an incredibly powerful tool for testing interactions with your controllers. It lets you test HTTP requests on the fly and spot issues that might only show up in real usage. With it you can be confident your API behaves correctly even before you deploy to production.
GO TO FULL VERSION