Spring Boot Testing
Spring Boot provides powerful test annotations that load only the parts of the application you need.
Test Slices
| Annotation | Loads | Use For |
|---|---|---|
@SpringBootTest |
Full context | Integration tests |
@WebMvcTest |
Controllers only | Controller/API tests |
@DataJpaTest |
JPA + H2 | Repository tests |
@MockBean |
Replace bean with mock | Isolating dependencies |
Unit Testing Services
@ExtendWith(MockitoExtension.class)
class BookServiceTest {
@Mock
private BookRepository bookRepository;
@InjectMocks
private BookService bookService;
@Test
void shouldCreateBook() {
CreateBookRequest request = new CreateBookRequest("Clean Code", "Robert Martin", "978-0132350884", new BigDecimal("39.99"));
Book saved = new Book("Clean Code", "Robert Martin", "978-0132350884", new BigDecimal("39.99"));
saved.setId(1L);
when(bookRepository.existsByIsbn(anyString())).thenReturn(false);
when(bookRepository.save(any(Book.class))).thenReturn(saved);
BookResponse result = bookService.create(request);
assertEquals("Clean Code", result.title());
assertEquals(1L, result.id());
verify(bookRepository).save(any(Book.class));
}
@Test
void shouldThrowWhenBookNotFound() {
when(bookRepository.findById(99L)).thenReturn(Optional.empty());
assertThrows(ResourceNotFoundException.class,
() -> bookService.findById(99L));
}
}
@WebMvcTest — Controller Tests
@WebMvcTest(BookController.class)
class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookService bookService;
@Test
void shouldReturnAllBooks() throws Exception {
List<BookResponse> books = List.of(
new BookResponse(1L, "Clean Code", "Robert Martin", "978-0132350884",
new BigDecimal("39.99"), "PUBLISHED", LocalDateTime.now())
);
when(bookService.findAll(any())).thenReturn(new PageImpl<>(books));
mockMvc.perform(get("/api/books"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content[0].title").value("Clean Code"))
.andExpect(jsonPath("$.content[0].author").value("Robert Martin"));
}
@Test
void shouldReturn404WhenNotFound() throws Exception {
when(bookService.findById(99L))
.thenThrow(new ResourceNotFoundException("Book", 99L));
mockMvc.perform(get("/api/books/99"))
.andExpect(status().isNotFound());
}
@Test
void shouldValidateCreateRequest() throws Exception {
String invalidJson = """
{"title": "", "author": "", "price": -10}
""";
mockMvc.perform(post("/api/books")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson))
.andExpect(status().isBadRequest());
}
}
@DataJpaTest — Repository Tests
@DataJpaTest
class BookRepositoryTest {
@Autowired
private BookRepository bookRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void shouldFindByAuthor() {
Book book = new Book("Clean Code", "Robert Martin", "978-0132350884", new BigDecimal("39.99"));
entityManager.persistAndFlush(book);
List<Book> found = bookRepository.findByAuthor("Robert Martin");
assertEquals(1, found.size());
assertEquals("Clean Code", found.get(0).getTitle());
}
@Test
void shouldCheckIsbnExists() {
Book book = new Book("Test", "Author", "123-4567890123", new BigDecimal("10.00"));
entityManager.persistAndFlush(book);
assertTrue(bookRepository.existsByIsbn("123-4567890123"));
assertFalse(bookRepository.existsByIsbn("000-0000000000"));
}
}
@SpringBootTest — Integration Tests
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class BookIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldCreateAndRetrieveBook() {
CreateBookRequest request = new CreateBookRequest(
"Integration Test Book", "Tester", "999-0000000001", new BigDecimal("25.00"));
ResponseEntity<BookResponse> createResponse = restTemplate.postForEntity(
"/api/books", request, BookResponse.class);
assertEquals(HttpStatus.CREATED, createResponse.getStatusCode());
Long bookId = createResponse.getBody().id();
ResponseEntity<BookResponse> getResponse = restTemplate.getForEntity(
"/api/books/" + bookId, BookResponse.class);
assertEquals("Integration Test Book", getResponse.getBody().title());
}
}
Key Takeaways
- Unit tests:
@ExtendWith(MockitoExtension.class)+@Mock— no Spring context. @WebMvcTest: test controllers with MockMvc — fast, isolated.@DataJpaTest: test repositories with embedded H2.@SpringBootTest: full integration test — slow but thorough.- Use
@MockBeanto replace real beans with mocks in Spring tests.
What’s Next?
Next: Dockerizing Spring Boot — Dockerfile, multi-stage builds, and Docker Compose.