git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/spring-boot-testing" ~/.claude/skills/diegosouzapw-awesome-omni-skill-spring-boot-testing && rm -rf "$T"
skills/development/spring-boot-testing/SKILL.mdSpring Boot Testing Setup Guide
This skill provides comprehensive documentation for setting up unit and integration testing in Spring Boot applications, emphasizing auto-configuration and best practices from the Baeldung reference patterns.
Overview
Spring Boot provides powerful testing support with auto-configured test annotations that bootstrap only the necessary parts of the application context. This approach ensures fast, focused tests while maintaining the benefits of Spring's dependency injection.
Note: For Spring Boot 3.4+, use
@MockitoBean instead of @MockBean for mocking dependencies. @MockitoBean provides better performance and more consistent behavior with the latest Mockito features.
Dependencies
Add these essential testing dependencies to your
pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>test</scope> </dependency>
The
spring-boot-starter-test includes:
- JUnit 5
- Mockito
- MockitoBean (Spring Boot 3.4+)
- AssertJ
- Hamcrest
- Spring Test & Spring Boot Test
Testing Strategy
1. Unit Tests
- Purpose: Test individual components in isolation
- Speed: Fast
- Dependencies: Mocked
- Recommended Annotations:
,@ExtendWith(SpringExtension.class)
,@WebMvcTest@DataJpaTest
2. Integration Tests
- Purpose: Test interaction between layers
- Speed: Slower
- Dependencies: Real or test database
- Recommended Annotations:
@SpringBootTest
Auto-Configured Test Annotations
Controller Layer Testing
Use
@WebMvcTest for focused controller tests:
@ExtendWith(SpringExtension.class) @WebMvcTest(EmployeeRestController.class) public class EmployeeRestControllerTest { @Autowired private MockMvc mvc; @MockitoBean // Use MockitoBean for Spring Boot 3.4+ private EmployeeService service; @Test public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { Employee alex = new Employee("alex"); List<Employee> allEmployees = Arrays.asList(alex); given(service.getAllEmployees()).willReturn(allEmployees); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].name", is(alex.getName()))); } }
Benefits:
- Auto-configures MockMvc
- Loads only controller-related beans
- Mocks dependencies with
(Spring Boot 3.4+) or@MockitoBean
(older versions)@MockBean - Fast execution
Persistence Layer Testing
Use
@DataJpaTest for repository tests:
@ExtendWith(SpringExtension.class) @DataJpaTest public class EmployeeRepositoryTest { @Autowired private TestEntityManager entityManager; @Autowired private EmployeeRepository employeeRepository; @Test public void whenFindByName_thenReturnEmployee() { Employee alex = new Employee("alex"); entityManager.persist(alex); entityManager.flush(); Employee found = employeeRepository.findByName(alex.getName()); assertThat(found.getName()).isEqualTo(alex.getName()); } }
Auto-configuration includes:
- In-memory H2 database
- Hibernate, Spring Data, DataSource
- Entity scanning
- SQL logging
Service Layer Testing
Use
@ExtendWith with @MockBean for service tests:
@ExtendWith(SpringExtension.class) public class EmployeeServiceTest { @MockitoBean // Use MockitoBean for Spring Boot 3.4+ private EmployeeRepository employeeRepository; @Autowired private EmployeeService employeeService; @BeforeEach public void setUp() { Employee alex = new Employee("alex"); when(employeeRepository.findByName(alex.getName())).thenReturn(alex); } @Test public void whenValidName_thenEmployeeShouldBeFound() { String name = "alex"; Employee found = employeeService.getEmployeeByName(name); assertThat(found.getName()).isEqualTo(name); } }
Full Integration Testing
Use
@SpringBootTest for end-to-end tests:
@ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @TestPropertySource(locations = "classpath:application-integrationtest.properties") public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @Autowired private EmployeeRepository repository; @Test public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { createTestEmployee("bob"); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].name", is("bob"))); } private void createTestEmployee(String name) { Employee employee = new Employee(name); repository.save(employee); } }
Test Configuration
Test-Specific Configuration
Use
@TestConfiguration for test-specific beans:
@TestConfiguration public class EmployeeServiceTestConfig { @Bean @Primary public EmployeeService employeeService() { return new EmployeeServiceImpl() { // Test-specific implementation }; } }
Import in tests:
@ExtendWith(SpringExtension.class) @Import(EmployeeServiceTestConfig.class) public class EmployeeServiceTest { // Test implementation }
Test Properties
Create test-specific configuration files. Spring Boot supports both
.properties and .yml formats:
For
format (application-integrationtest.properties):.properties
spring.datasource.url=jdbc:h2:mem:test spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=create-drop logging.level.org.hibernate.SQL=DEBUG
For
format (application-integrationtest.yml):.yml
spring: datasource: url: jdbc:h2:mem:test jpa: properties: hibernate: dialect: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: create-drop logging: level: org.hibernate.SQL: DEBUG
Note: Use the same format as your main application configuration. If your project uses
application.yml, create application-integrationtest.yml for consistency.
Specialized Auto-Configured Tests
WebFlux Testing
@WebFluxTest(HomeController.class) public class HomeControllerTest { // Test reactive controllers }
JSON Testing
@JsonTest public class EmployeeJsonTest { @Autowired private JacksonTester<Employee> json; @Test public void employeeSerializationTest() throws IOException { Employee employee = new Employee("John"); assertThat(json.write(employee)).isEqualToJson("expected.json"); } }
REST Client Testing
@RestClientTest(EmployeeClient.class) public class EmployeeClientTest { @Autowired private MockRestServiceServer server; @Autowired private EmployeeClient client; }
Database Testing
JDBC Testing
@JdbcTest public class EmployeeJdbcTest { @Autowired private JdbcTemplate jdbcTemplate; }
MongoDB Testing
@DataMongoTest public class EmployeeMongoTest { @Autowired private MongoTemplate mongoTemplate; }
Best Practices
1. Test Organization
- Keep unit tests in
src/test/java - Separate integration tests with
annotation profiles@Test - Use descriptive test names
2. Auto-Configuration Priority
Always prefer auto-configured test annotations:
- Use
instead of@WebMvcTest
for controller tests@SpringBootTest - Use
instead of@DataJpaTest
for repository tests@SpringBootTest - Use
only when necessary@SpringBootTest
3. Mocking Strategy
- Mock external dependencies with
(Spring Boot 3.4+) or@MockitoBean
(older versions)@MockBean - Use
for test-specific beans@TestConfiguration - Avoid mocking the layer you're testing
4. Test Data Management
- Use
for JPA testsTestEntityManager - Clean up test data in
@AfterEach - Use
for rollback behavior@Transactional
5. Performance Considerations
- Run unit tests separately from integration tests
- Use in-memory databases for testing
- Limit
usage@SpringBootTest
Test Execution Profiles
Maven Configuration
<profiles> <profile> <id>unit-tests</id> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <includes> <include>**/*Test.java</include> </includes> <excludes> <exclude>**/*IntegrationTest.java</exclude> </excludes> </configuration> </plugin> </plugins> </build> </profile> <profile> <id>integration-tests</id> <build> <plugins> <plugin> <artifactId>maven-failsafe-plugin</artifactId> <configuration> <includes> <include>**/*IntegrationTest.java</include> </includes> </configuration> </plugin> </plugins> </build> </profile> </profiles>
Running Tests
# Unit tests only mvn test -Punit-tests # Integration tests only mvn verify -Pintegration-tests # All tests mvn test
Common Patterns
Service Layer with Repository Mock
@ExtendWith(SpringExtension.class) public class EmployeeServiceTest { @MockitoBean // Use MockitoBean for Spring Boot 3.4+ private EmployeeRepository repository; @InjectMocks @Autowired private EmployeeService service; @Test public void whenFindById_thenReturnEmployee() { // Given Employee employee = new Employee("John"); when(repository.findById(1L)).thenReturn(Optional.of(employee)); // When Employee found = service.findById(1L); // Then assertThat(found.getName()).isEqualTo("John"); } }
Controller with Service Mock
@WebMvcTest(EmployeeController.class) public class EmployeeControllerTest { @Autowired private MockMvc mockMvc; @MockitoBean // Use MockitoBean for Spring Boot 3.4+ private EmployeeService service; @Test public void getEmployee_shouldReturnEmployee() throws Exception { Employee employee = new Employee("John"); when(service.findById(1L)).thenReturn(employee); mockMvc.perform(get("/employees/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name", is("John"))); } }
Repository Integration Test
@DataJpaTest @TestPropertySource(properties = { "spring.jpa.hibernate.ddl-auto=create-drop" }) public class EmployeeRepositoryTest { @Autowired private TestEntityManager entityManager; @Autowired private EmployeeRepository repository; @Test public void findByName_shouldReturnEmployee() { Employee employee = new Employee("John"); entityManager.persistAndFlush(employee); Optional<Employee> found = repository.findByName("John"); assertThat(found).isPresent(); assertThat(found.get().getName()).isEqualTo("John"); } }
Conclusion
Spring Boot's auto-configured test annotations provide a powerful, efficient way to structure tests. By choosing the right annotation for each test type and leveraging auto-configuration, you can create fast, maintainable test suites that provide comprehensive coverage while minimizing setup complexity.
Key takeaways:
- Prefer auto-configured annotations over manual configuration
- Choose the right test slice for the layer you're testing
- Mock dependencies appropriately with
(Spring Boot 3.4+) or@MockitoBean
(older versions)@MockBean - Separate test types for better performance and organization
- Use test-specific configuration for specialized test needs