spring-boot-test

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-boot-test for comprehensive documentation.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "spring-boot-test" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-spring-boot-test

Spring Boot Testing

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-boot-test for comprehensive documentation.

When NOT to Use This Skill

  • Pure Unit Tests - Use junit with Mockito for faster tests without Spring context

  • Integration Tests with Real Database - Use spring-boot-integration with Testcontainers

  • REST API Client Testing - Use rest-assured for HTTP testing

  • E2E Web Testing - Use Selenium or Playwright

  • Microservice Contract Testing - Use Spring Cloud Contract

Dependencies

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

Includes: JUnit 5, Mockito, AssertJ, Hamcrest, JSONPath, Spring Test

Test Annotations

@SpringBootTest - Full Context

@SpringBootTest class ApplicationTest { @Autowired private UserService userService;

@Test
void contextLoads() {
    assertThat(userService).isNotNull();
}

}

// With web environment @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class WebApplicationTest { @LocalServerPort private int port;

@Autowired
private TestRestTemplate restTemplate;

@Test
void healthCheck() {
    ResponseEntity&#x3C;String> response = restTemplate
        .getForEntity("/actuator/health", String.class);
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}

}

Slice Tests (Faster, Focused)

Annotation Layer Auto-configured

@WebMvcTest

Controllers MockMvc, Jackson

@DataJpaTest

JPA Repositories TestEntityManager, DataSource

@DataMongoTest

MongoDB MongoTemplate

@JsonTest

JSON serialization JacksonTester

@RestClientTest

REST clients MockRestServiceServer

// Controller test - only loads web layer @WebMvcTest(UserController.class) class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

@Test
void getUser_ReturnsUser() throws Exception {
    when(userService.findById(1L))
        .thenReturn(Optional.of(new User(1L, "John")));

    mockMvc.perform(get("/api/users/1"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.name").value("John"));
}

}

// Repository test - uses embedded database @DataJpaTest class UserRepositoryTest {

@Autowired
private TestEntityManager entityManager;

@Autowired
private UserRepository repository;

@Test
void findByEmail_ReturnsUser() {
    User user = new User("test@example.com", "Test User");
    entityManager.persistAndFlush(user);

    Optional&#x3C;User> found = repository.findByEmail("test@example.com");

    assertThat(found).isPresent()
        .hasValueSatisfying(u -> assertThat(u.getName()).isEqualTo("Test User"));
}

}

MockMvc

Basic Requests

@WebMvcTest(UserController.class) class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

// GET request
@Test
void getUsers() throws Exception {
    when(userService.findAll()).thenReturn(List.of(
        new User(1L, "Alice"),
        new User(2L, "Bob")
    ));

    mockMvc.perform(get("/api/users")
            .accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andExpect(jsonPath("$", hasSize(2)))
        .andExpect(jsonPath("$[0].name").value("Alice"));
}

// POST request with JSON body
@Test
void createUser() throws Exception {
    CreateUserRequest request = new CreateUserRequest("John", "john@example.com");
    User createdUser = new User(1L, "John", "john@example.com");

    when(userService.create(any())).thenReturn(createdUser);

    mockMvc.perform(post("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content("""
                {
                    "name": "John",
                    "email": "john@example.com"
                }
                """))
        .andExpect(status().isCreated())
        .andExpect(header().exists("Location"))
        .andExpect(jsonPath("$.id").value(1));
}

// PUT request
@Test
void updateUser() throws Exception {
    mockMvc.perform(put("/api/users/{id}", 1)
            .contentType(MediaType.APPLICATION_JSON)
            .content("""
                {"name": "Updated Name"}
                """))
        .andExpect(status().isOk());

    verify(userService).update(eq(1L), any());
}

// DELETE request
@Test
void deleteUser() throws Exception {
    mockMvc.perform(delete("/api/users/{id}", 1))
        .andExpect(status().isNoContent());

    verify(userService).delete(1L);
}

}

With Authentication

@WebMvcTest(AdminController.class) @Import(SecurityConfig.class) class AdminControllerTest {

@Autowired
private MockMvc mockMvc;

// Using @WithMockUser
@Test
@WithMockUser(roles = "ADMIN")
void adminEndpoint_WithAdminRole_Succeeds() throws Exception {
    mockMvc.perform(get("/api/admin/dashboard"))
        .andExpect(status().isOk());
}

@Test
@WithMockUser(roles = "USER")
void adminEndpoint_WithUserRole_Forbidden() throws Exception {
    mockMvc.perform(get("/api/admin/dashboard"))
        .andExpect(status().isForbidden());
}

// Using JWT token
@Test
void withJwtToken() throws Exception {
    String token = jwtTokenProvider.createToken("admin", List.of("ROLE_ADMIN"));

    mockMvc.perform(get("/api/admin/dashboard")
            .header("Authorization", "Bearer " + token))
        .andExpect(status().isOk());
}

}

Mocking

@MockBean

@SpringBootTest class OrderServiceTest {

@Autowired
private OrderService orderService;

@MockBean  // Replaces bean in context with mock
private PaymentGateway paymentGateway;

@MockBean
private InventoryService inventoryService;

@Test
void placeOrder_WhenPaymentSucceeds_CreatesOrder() {
    when(inventoryService.checkStock(any())).thenReturn(true);
    when(paymentGateway.charge(any())).thenReturn(PaymentResult.success());

    Order order = orderService.placeOrder(new OrderRequest(...));

    assertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);
    verify(paymentGateway).charge(any());
}

@Test
void placeOrder_WhenPaymentFails_ThrowsException() {
    when(paymentGateway.charge(any()))
        .thenThrow(new PaymentException("Card declined"));

    assertThatThrownBy(() -> orderService.placeOrder(new OrderRequest(...)))
        .isInstanceOf(PaymentException.class)
        .hasMessage("Card declined");
}

}

@SpyBean

@SpringBootTest class NotificationServiceTest {

@Autowired
private NotificationService notificationService;

@SpyBean  // Wraps real bean, allows partial mocking
private EmailSender emailSender;

@Test
void sendNotification_CallsEmailSender() {
    notificationService.notify(user, "Hello");

    verify(emailSender).send(eq(user.getEmail()), any());
}

@Test
void sendNotification_WhenEmailFails_LogsError() {
    doThrow(new EmailException("SMTP error"))
        .when(emailSender).send(any(), any());

    // Method should handle exception gracefully
    assertThatCode(() -> notificationService.notify(user, "Hello"))
        .doesNotThrowAnyException();
}

}

Test Configuration

Test Properties

@SpringBootTest @TestPropertySource(properties = { "app.feature.enabled=true", "app.external.url=http://localhost:8080" }) class FeatureTest { }

// Or use test profile @SpringBootTest @ActiveProfiles("test") class ProfileTest { }

application-test.yml

spring: datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create-drop

app: external: url: http://localhost:${wiremock.server.port}

Custom Test Configuration

@TestConfiguration public class TestConfig {

@Bean
@Primary
public Clock testClock() {
    return Clock.fixed(
        Instant.parse("2025-01-15T10:00:00Z"),
        ZoneId.of("UTC")
    );
}

@Bean
@Primary
public PaymentGateway testPaymentGateway() {
    return new FakePaymentGateway();
}

}

@SpringBootTest @Import(TestConfig.class) class TimeBasedFeatureTest { }

AssertJ Assertions

// Basic assertions assertThat(user.getName()).isEqualTo("John"); assertThat(user.getAge()).isGreaterThan(18); assertThat(user.getEmail()).contains("@").endsWith(".com");

// Collection assertions assertThat(users) .hasSize(3) .extracting(User::getName) .containsExactly("Alice", "Bob", "Charlie");

// Exception assertions assertThatThrownBy(() -> service.process(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("null");

// Optional assertions assertThat(repository.findById(1L)) .isPresent() .hasValueSatisfying(user -> assertThat(user.getName()).isEqualTo("John") );

// Soft assertions (collect all failures) SoftAssertions.assertSoftly(softly -> { softly.assertThat(user.getName()).isEqualTo("John"); softly.assertThat(user.getEmail()).contains("@"); softly.assertThat(user.getAge()).isPositive(); });

Test Data Builders

public class UserTestBuilder { private Long id = 1L; private String name = "Test User"; private String email = "test@example.com"; private UserRole role = UserRole.USER;

public static UserTestBuilder aUser() {
    return new UserTestBuilder();
}

public UserTestBuilder withId(Long id) {
    this.id = id;
    return this;
}

public UserTestBuilder withName(String name) {
    this.name = name;
    return this;
}

public UserTestBuilder withEmail(String email) {
    this.email = email;
    return this;
}

public UserTestBuilder withRole(UserRole role) {
    this.role = role;
    return this;
}

public UserTestBuilder asAdmin() {
    this.role = UserRole.ADMIN;
    return this;
}

public User build() {
    return new User(id, name, email, role);
}

}

// Usage User admin = aUser().withName("Admin").asAdmin().build(); User regularUser = aUser().build();

Anti-Patterns

Anti-Pattern Why It's Bad Solution

Using @SpringBootTest for all tests Extremely slow Use slice tests (@WebMvcTest, @DataJpaTest)

Not using @MockBean Testing real beans Mock external dependencies

Hardcoding ports in tests Port conflicts Use @LocalServerPort with RANDOM_PORT

Testing private methods Coupled to implementation Test through controller/service API

Not isolating test data Tests interfere Use @Transactional or cleanup in @AfterEach

Ignoring @Sql scripts Manual setup duplication Use @Sql for test data setup

No test profiles Polluting dev/prod config Use @ActiveProfiles("test")

Quick Troubleshooting

Problem Likely Cause Solution

"Unable to find @SpringBootConfiguration" Main class not found Add @SpringBootTest(classes = App.class)

Test very slow Using @SpringBootTest unnecessarily Use slice tests (@WebMvcTest, etc.)

"No qualifying bean" Missing @MockBean Add @MockBean for dependencies

Port already in use Hardcoded port Use webEnvironment = RANDOM_PORT

"Could not autowire" Bean not in test context Check component scan or add @Import

Flaky test Database state not reset Use @Transactional or @DirtiesContext

Reference

  • Quick Reference: Annotations

  • Spring Boot Testing Guide

  • See also: spring-boot-integration for Testcontainers

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review