Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Provides comprehensive testing patterns for Spring Boot applications covering unit, integration, slice, and container-based testing with JUnit 5, Mockito, Testcontainers, and performance optimization. Use when writing tests, @Test methods, @MockBean mocks, or implementing test suites for Spring Boot applications.
.claude/skills/giuseppe-trisciuoglio-spring-boot-test-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 176% | 0% |
| case-13 | ✓→✗ | ▼ Worse | 72% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 105% | 0% |
Comprehensive guidance for writing robust test suites for Spring Boot applications using JUnit 5, Mockito, Testcontainers, and performance-optimized slice testing patterns.
@WebMvcTest or MockMvc@ServiceConnection for container management in Spring Boot 3.5+| Test Type | Annotation | Target Time | Use Case | |-----------|------------|-------------|----------| | Unit Tests | @ExtendWith(MockitoExtension.class) | < 50ms | Business logic without Spring context | | Repository Tests | @DataJpaTest | < 100ms | Database operations with minimal context | | Controller Tests | @WebMvcTest / @WebFluxTest | < 100ms | REST API layer testing | | Integration Tests | @SpringBootTest | < 500ms | Full application context with containers | | Testcontainers | @ServiceConnection / @Testcontainers | Varies | Real database/message broker containers |
Spring Boot Test:
@SpringBootTest — Full application context (use sparingly)@DataJpaTest — JPA components only (repositories, entities)@WebMvcTest — MVC layer only (controllers, @ControllerAdvice)@WebFluxTest — WebFlux layer only (reactive controllers)@JsonTest — JSON serialization components onlyTestcontainers:
@ServiceConnection — Wire Testcontainer to Spring Boot (3.5+)@DynamicPropertySource — Register dynamic properties at runtime@Testcontainers — Enable Testcontainers lifecycle managementTest business logic with mocked dependencies:
java@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void shouldFindUserByIdWhenExists() { when(userRepository.findById(1L)).thenReturn(Optional.of(user)); Optional<User> result = userService.findById(1L); assertThat(result).isPresent(); verify(userRepository).findById(1L); } }
See unit-testing.md for advanced patterns.
Use focused test slices for specific layers:
java@DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @TestContainerConfig class UserRepositoryIntegrationTest { @Autowired private UserRepository userRepository; @Test void shouldSaveAndRetrieveUser() { User saved = userRepository.save(user); assertThat(userRepository.findByEmail("test@example.com")).isPresent(); } }
See slice-testing.md for all slice patterns.
Test controllers with MockMvc:
java@WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void shouldGetUserById() throws Exception { mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.email").value("test@example.com")); } }
@ServiceConnectionConfigure containers with Spring Boot 3.5+:
java@TestConfiguration public class TestContainerConfig { @Bean @ServiceConnection public PostgreSQLContainer<?> postgresContainer() { return new PostgreSQLContainer<>("postgres:16-alpine"); } }
Apply with @Import(TestContainerConfig.class) on test classes. See testcontainers-setup.md for detailed configuration.
Include required testing dependencies:
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>1.19.0</version> <scope>test</scope> </dependency>
See test-dependencies.md for complete dependency list.
Set up GitHub Actions for automated testing:
yamlname: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest services: docker: image: docker:20-dind steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: 'temurin' - name: Run tests run: ./mvnw test
See ci-cd-configuration.md for full CI/CD patterns.
After implementing tests, verify:
docker ps (look for testcontainer images)@ServiceConnectionjava@SpringBootTest @Import(TestContainerConfig.class) class OrderServiceIntegrationTest { @Autowired private OrderService orderService; @Autowired private UserRepository userRepository; @Test void shouldCreateOrderForExistingUser() { User user = userRepository.save(User.builder() .email("order-test@example.com") .build()); Order order = orderService.createOrder(user.getId(), List.of( new OrderItem("SKU-001", 2) )); assertThat(order.getId()).isNotNull(); assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING); } }
@DataJpaTest with Real Databasejava@DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @TestContainerConfig class UserRepositoryTest { @Autowired private UserRepository userRepository; @Test void shouldFindByEmail() { userRepository.save(User.builder() .email("jpa-test@example.com") .build()); assertThat(userRepository.findByEmail("jpa-test@example.com")) .isPresent(); } }
See workflow-patterns.md for complete end-to-end examples.
@DataJpaTest for repositories, @WebMvcTest for controllers, @SpringBootTest only for full integration@ServiceConnection on Spring Boot 3.5+ for cleaner container management over @DynamicPropertySource@BeforeEachwithReuse(true) + TESTCONTAINERS_REUSE_ENABLE=true)@DirtiesContext: Forces context rebuild, significantly hurts performance@DirtiesContext unless absolutely necessary (forces context rebuild)@MockBean with different configurations (creates separate contexts)@TestPropertySource (creates separate contexts)@SpringBootTest for unit tests; use plain Mockito instead@MockBean configurations| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 9,773 | 9,785 | +0% | 1 | 1 | 0% | 1,661 | 3,669 | +121% | 0 | 0 | — |
case-02 | pass→pass | 10,016 | 8,185 | -18% | 1 | 1 | 0% | 1,883 | 3,868 | +105% | 0 | 0 | — |
case-03 | pass→pass | 6,735 | 6,659 | -1% | 1 | 1 | 0% | 1,340 | 3,443 | +157% | 0 | 0 | — |
case-04 | fail→pass | 10,386 | 11,802 | +14% | 1 | 1 | 0% | 2,089 | 3,869 | +85% | 0 | 0 | — |
case-05 | pass→pass | 4,165 | 3,919 | -6% | 1 | 1 | 0% | 693 | 2,834 | +309% | 0 | 0 | — |
case-06 | pass→pass | 8,011 | 5,992 | -25% | 1 | 1 | 0% | 1,221 | 3,106 | +154% | 0 | 0 | — |
case-07 | pass→pass | 2,937 | 3,982 | +36% | 1 | 1 | 0% | 494 | 2,832 | +473% | 0 | 0 | — |
case-08 | pass→pass | 4,190 | 4,394 | +5% | 1 | 1 | 0% | 717 | 2,921 | +307% | 0 | 0 | — |
case-09 | pass→pass | 3,360 | 3,408 | +1% | 1 | 1 | 0% | 635 | 2,832 | +346% | 0 | 0 | — |
case-10 | fail→pass | 12,843 | 3,949 | -69% | 1 | 1 | 0% | 1,979 | 2,772 | +40% | 0 | 0 | — |
case-11 | fail→fail | 8,860 | 7,834 | -12% | 1 | 1 | 0% | 1,515 | 3,609 | +138% | 0 | 0 | — |
case-12 | pass→pass | 4,850 | 5,242 | +8% | 1 | 1 | 0% | 820 | 3,087 | +276% | 0 | 0 | — |
case-13 | pass→fail | 9,726 | 3,406 | -65% | 1 | 1 | 0% | 1,532 | 2,641 | +72% | 0 | 0 | — |
case-14 | pass→pass | 13,992 | 11,916 | -15% | 1 | 1 | 0% | 2,287 | 4,358 | +91% | 0 | 0 | — |
case-15 | pass→pass | 15,014 | 24,499 | +63% | 1 | 1 | 0% | 2,333 | 4,291 | +84% | 0 | 0 | — |
case-16 | pass→pass | 3,574 | 3,486 | -2% | 1 | 1 | 0% | 560 | 2,635 | +371% | 0 | 0 | — |
case-17 | fail→pass | 5,471 | 3,229 | -41% | 1 | 1 | 0% | 959 | 2,645 | +176% | 0 | 0 | — |
case-18 | pass→pass | 7,663 | 6,338 | -17% | 1 | 1 | 0% | 1,590 | 3,373 | +112% | 0 | 0 | — |
case-19 | pass→pass | 3,066 | 3,341 | +9% | 1 | 1 | 0% | 517 | 2,685 | +419% | 0 | 0 | — |
case-20 | pass→pass | 13,035 | 16,280 | +25% | 1 | 1 | 0% | 2,587 | 5,362 | +107% | 0 | 0 | — |
case-21 | pass→pass | 14,943 | 12,117 | -19% | 1 | 1 | 0% | 2,935 | 4,510 | +54% | 0 | 0 | — |
case-22 | pass→pass | 8,281 | 8,816 | +6% | 1 | 1 | 0% | 1,712 | 3,933 | +130% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.