Files
awesome-copilot/skills/spring-boot-testing/references/test-slices-overview.md
Kartik Dhiman e4fc57f204 feat: add spring-boot-testing skill for Spring Boot 4.0 (#1085)
- Introduced MockMvcTester for AssertJ-style assertions in Spring MVC testing.
- Added @RestClientTest for testing REST clients with MockRestServiceServer.
- Implemented RestTestClient as a modern alternative to TestRestTemplate.
- Documented migration steps from Spring Boot 3.x to 4.0, including dependency and annotation changes.
- Created an overview of test slices to guide testing strategies.
- Included Testcontainers setup for JDBC testing with PostgreSQL and MySQL.
- Enhanced @WebMvcTest documentation with examples for various HTTP methods and validation.
2026-03-20 10:24:37 +11:00

5.4 KiB

Test Slices Overview

Quick reference for selecting the right Spring Boot test slice.

Decision Matrix

Annotation Use When Loads Speed
None (plain JUnit) Testing pure business logic Nothing Fastest
@WebMvcTest Controller + HTTP layer Controllers, MVC, Jackson Fast
@DataJpaTest Repository queries Repositories, JPA, DataSource Fast
@RestClientTest REST client code RestTemplate/RestClient, Jackson Fast
@JsonTest JSON serialization ObjectMapper only Fastest slice
@WebFluxTest Reactive controllers Controllers, WebFlux Fast
@DataJdbcTest JDBC repositories Repositories, JDBC Fast
@DataMongoTest MongoDB repositories Repositories, MongoDB Fast
@DataRedisTest Redis repositories Repositories, Redis Fast
@SpringBootTest Full integration Entire application Slow

Selection Guide

Use NO Annotation (Plain Unit Test)

class PriceCalculatorTest {
  private PriceCalculator calculator = new PriceCalculator();
  
  @Test
  void shouldApplyDiscount() {
    var result = calculator.applyDiscount(100, 0.1);
    assertThat(result).isEqualTo(new BigDecimal("90.00"));
  }
}

When: Pure business logic, no dependencies or simple dependencies mockable via constructor injection.

Use @WebMvcTest

@WebMvcTest(OrderController.class)
class OrderControllerTest {
  @Autowired private MockMvcTester mvc;
  @MockitoBean private OrderService orderService;
}

When: Testing request mapping, validation, JSON mapping, security, filters.

What you get: MockMvc, ObjectMapper, Spring Security (if present), exception handlers.

Use @DataJpaTest

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryTest {
  @Container
  static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
}

When: Testing custom JPA queries, entity mappings, transaction behavior, cascade operations.

What you get: Repository beans, EntityManager, TestEntityManager, transaction support.

Use @RestClientTest

@RestClientTest(WeatherService.class)
class WeatherServiceTest {
  @Autowired private WeatherService weatherService;
  @Autowired private MockRestServiceServer server;
}

When: Testing REST clients that call external APIs.

What you get: MockRestServiceServer to stub HTTP responses.

Use @JsonTest

@JsonTest
class OrderJsonTest {
  @Autowired private JacksonTester<Order> json;
}

When: Testing custom serializers/deserializers, complex JSON mapping.

Use @SpringBootTest

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class OrderIntegrationTest {
  @Autowired private RestTestClient restClient;
}

When: Testing full request flow, security filters, database interactions together.

What you get: Full application context, embedded server (optional), real beans.

Common Mistakes

  1. Using @SpringBootTest for everything - Slows down your test suite unnecessarily
  2. @WebMvcTest without mocking services - Causes context loading failures
  3. @DataJpaTest with @MockBean - Defeats the purpose (you want real repositories)
  4. Multiple slices in one test - Each slice is a separate test class

Java 25 Features in Tests

Records for Test Data

record OrderRequest(String product, int quantity) {}
record OrderResponse(Long id, String status, BigDecimal total) {}

Pattern Matching in Tests

@Test
void shouldHandleDifferentOrderTypes() {
  var order = orderService.create(new OrderRequest("Product", 2));
  
  switch (order) {
    case PhysicalOrder po -> assertThat(po.getShippingAddress()).isNotNull();
    case DigitalOrder do_ -> assertThat(do_.getDownloadLink()).isNotNull();
    default -> throw new IllegalStateException("Unknown order type");
  }
}

Text Blocks for JSON

@Test
void shouldParseComplexJson() {
  var json = """
    {
      "id": 1,
      "status": "PENDING",
      "items": [
        {"product": "Laptop", "price": 999.99},
        {"product": "Mouse", "price": 29.99}
      ]
    }
    """;
  
  assertThat(mvc.post().uri("/orders")
    .contentType(APPLICATION_JSON)
    .content(json))
    .hasStatus(CREATED);
}

Sequenced Collections

@Test
void shouldReturnOrdersInSequence() {
  var orders = orderRepository.findAll();
  
  assertThat(orders.getFirst().getStatus()).isEqualTo("NEW");
  assertThat(orders.getLast().getStatus()).isEqualTo("COMPLETED");
  assertThat(orders.reversed().getFirst().getStatus()).isEqualTo("COMPLETED");
}

Dependencies by Slice

<!-- WebMvcTest -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webmvc-test</artifactId>
  <scope>test</scope>
</dependency>

<!-- DataJpaTest -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- RestClientTest -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-restclient-test</artifactId>
  <scope>test</scope>
</dependency>

<!-- Testcontainers -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-testcontainers</artifactId>
  <scope>test</scope>
</dependency>