← All articles
Backend1 min read

Testing Spring Boot Applications

Unit tests, slice tests, and full integration tests with Testcontainers. Build a testing pyramid that stays fast and reliable.

S

Swapnika Voora

Author

A good test suite gives you the confidence to refactor. Spring Boot supports a layered approach: fast unit tests at the base, focused slice tests in the middle, and a few full integration tests at the top.

Plain unit tests

The fastest tests need no Spring context at all — construct the class and pass in mocks.

PriceCalculatorTest.java
class PriceCalculatorTest {
 
    @Test
    void appliesDiscount() {
        var calculator = new PriceCalculator();
        var result = calculator.apply(new BigDecimal("100"), 0.1);
        assertEquals(new BigDecimal("90.0"), result);
    }
}

Slice tests

Slice annotations load only part of the context. @WebMvcTest boots the web layer without touching the database.

ProductControllerTest.java
@WebMvcTest(ProductController.class)
class ProductControllerTest {
 
    @Autowired MockMvc mvc;
    @MockBean ProductService service;
 
    @Test
    void returnsProduct() throws Exception {
        when(service.findById(1L)).thenReturn(new ProductResponse(1L, "Book", TEN));
 
        mvc.perform(get("/api/products/1"))
           .andExpect(status().isOk())
           .andExpect(jsonPath("$.name").value("Book"));
    }
}

Integration tests with Testcontainers

For confidence that your queries actually work, run tests against a real database in a throwaway container.

OrderRepositoryIT.java
@SpringBootTest
@Testcontainers
class OrderRepositoryIT {
 
    @Container
    static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16");
 
    @DynamicPropertySource
    static void props(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", db::getJdbcUrl);
        registry.add("spring.datasource.username", db::getUsername);
        registry.add("spring.datasource.password", db::getPassword);
    }
}

Keep the pyramid balanced

  • Many unit tests — milliseconds each.
  • Some slice tests — one layer at a time.
  • Few integration tests — realistic but slower.

This shape keeps your feedback loop fast while still exercising the real stack where it counts.

#java#spring-boot#testing#testcontainers

More in Backend