ai-observability
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when writing tests of any kind — unit, slice, or integration. Covers test structure, naming conventions, Mockito patterns, @WebMvcTest, @DataJpaTest, and Testcontainers setup.
Claude Code
$ npx -y skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid --agent claude-code
Codex
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a codex -y
Cursor
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a cursor -y
Gemini CLI
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a gemini-cli -y
GitHub Copilot
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a github-copilot -y
OpenCode
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a opencode -y
Windsurf
$ npx skills add rrezartprebreza/spring-boot-skills --skill testing-pyramid -a windsurf -y
name: testing-pyramid description: > Use when writing tests of any kind — unit, slice, or integration. Covers test structure, naming conventions, Mockito patterns, @WebMvcTest, @DataJpaTest, and Testcontainers setup.
Unit Tests — fast domain logic checks without a Spring context Slice Tests — focused web or persistence infrastructure checks Integration Tests — cross-boundary behavior with real dependencies
Choose the mix by risk, not a fixed percentage. Use real database/Redis tests for concurrency, serialization and transaction semantics that mocks cannot establish. Preserve the project's JUnit/AssertJ and mocking conventions; changing assertion libraries is not a correctness fix.
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock private OrderRepository orderRepository;
@Mock private InventoryService inventoryService;
@InjectMocks private OrderService orderService;
@Test
void createOrder_whenItemsAvailable_shouldSaveAndReturnOrder() {
// Given
var request = new CreateOrderRequest("user@example.com", List.of(new OrderItemRequest(UUID.randomUUID(), 2)));
var savedOrder = Order.create("user@example.com");
when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);
doNothing().when(inventoryService).reserve(any());
// When
Order result = orderService.createOrder(request);
// Then
assertThat(result).isNotNull();
assertThat(result.getCustomerEmail()).isEqualTo("user@example.com");
verify(inventoryService).reserve(request.items());
verify(orderRepository).save(any(Order.class));
}
@Test
void createOrder_whenInventoryUnavailable_shouldThrowException() {
// Given
var request = new CreateOrderRequest("user@example.com", List.of());
doThrow(new InsufficientInventoryException("Out of stock"))
.when(inventoryService).reserve(any());
// When / Then
assertThatThrownBy(() -> orderService.createOrder(request))
.isInstanceOf(InsufficientInventoryException.class)
.hasMessage("Out of stock");
}
}// POSTs need .with(csrf()): @WebMvcTest applies Boot's default security (CSRF on);
// @WithMockUser only authenticates. Add @Import(SecurityConfig.class) to test your real rules.
// import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
@WebMvcTest(OrderController.class)
class OrderControllerTest {
@Autowired MockMvc mockMvc;
@Autowired ObjectMapper objectMapper;
@MockitoBean OrderService orderService; // Spring Boot 3.4+: @MockBean is deprecated
@Test
@WithMockUser(roles = "USER")
void createOrder_withValidRequest_shouldReturn201() throws Exception {
// Given
var request = new CreateOrderRequest("user@example.com",
List.of(new OrderItemRequest(UUID.randomUUID(), 1))); // items are @NotEmpty
var order = Order.create("user@example.com");
when(orderService.createOrder(any())).thenReturn(order);
// When / Then
mockMvc.perform(post("/api/v1/orders").with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.customerEmail").value("user@example.com"));
}
@Test
@WithMockUser
void createOrder_withInvalidRequest_shouldReturn400() throws Exception {
mockMvc.perform(post("/api/v1/orders").with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{}")) // missing required fields
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.error.code").value("VALIDATION_FAILED"));
}
}@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE) // use real DB (Testcontainers)
@Import(TestcontainersConfig.class)
class OrderRepositoryTest {
@Autowired OrderRepository orderRepository;
@Test
void findByStatus_shouldReturnMatchingOrders() {
// Given
var order1 = orderRepository.save(Order.create("a@example.com"));
var order2 = orderRepository.save(Order.create("b@example.com"));
order2.ship(); // change status
orderRepository.save(order2);
// When
List<Order> pending = orderRepository.findByStatus(OrderStatus.PENDING, Pageable.unpaged()).getContent();
// Then
assertThat(pending).hasSize(1);
assertThat(pending.get(0).getCustomerEmail()).isEqualTo("a@example.com");
}
}This application-level example assumes the first-party JWT setup and order API from the related skills. Supply `src/test/resources/fixtures/order-user-and-product.sql` for your application's schema: an enabled USER named `buyer@example.com` and an in-stock product with ID `00000000-0000-0000-0000-000000000001`. Run it only against the disposable test database. Adapt the fixtures and response DTO to your application.
Load the real persisted user before signing the token. `@WithMockUser` does not authenticate a request sent over HTTP to a random-port server. For OAuth2 resource servers, use a test issuer instead of introducing this application's JWT service.
// Shared config — reuse container across tests
@TestConfiguration(proxyBeanMethods = false)
public class TestcontainersConfig {
@Bean
@ServiceConnection
PostgreSQLContainer<?> postgresContainer() {
return new PostgreSQLContainer<>("postgres:16-alpine");
}
}
// Full integration tProduction-grade Claude Code and Codex skills for Spring Boot developers
Use when adding Spring AI-specific model observations, token usage, latency, externally configured cost attribution, advisor…
Use when versioning Spring MVC or WebFlux APIs in Spring Boot 3 / Spring Framework 6. Covers explicit URL, header, and media-type…
Use when introducing or correcting grouped Spring Boot configuration, typed property binding, validation, profiles or secret…
Use when packaging a Spring Boot 3 application as an OCI image or GraalVM native executable. Covers buildpacks, layered images,…
Use when evolving aggregate invariants, value objects or domain events in an existing DDD-style Spring Boot 3 application, or…
Use when implementing Kafka, RabbitMQ, Pulsar, or JMS producers and consumers in Spring Boot 3. Covers event contracts,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.