Back to Testing & Quality Assurance

spring-boot-test-patterns

Spring BoottestingJUnit 5MockitoTestcontainersintegration testsunit testsperformance optimization
282📄 MIT🕒 2026-06-15Source ↗

Install this skill

npx skills add giuseppe-trisciuoglio/developer-kit

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

This skill provides a structured framework for managing automated test suites within Spring Boot ecosystems. It focuses on balancing execution speed with verification depth by categorizing tests into pure unit tests, layer-specific slices, and full-stack integration suites using Testcontainers. The approach emphasizes avoiding unnecessary Spring context loading, which often slows down CI pipelines. By implementing patterns like @ServiceConnection for container lifecycle management and strategically applying slice annotations such as @DataJpaTest or @WebMvcTest, developers can maintain faster feedback loops. The guidance covers dependency configuration, architectural layering, and syntax patterns for Mockito and integration testing, ensuring that developers can verify business logic, data persistence, and HTTP endpoints with precision and minimal resource overhead.

When to Use This Skill

  • Writing verification logic for service layer methods using mocked repositories
  • Validating JPA entity mappings and custom repository queries against real database instances
  • Testing REST API endpoint status codes and payloads without spinning up the full application
  • Setting up repeatable integration tests for CI/CD environments

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • How do I write a repository unit test in Spring Boot?
  • Show me how to use Testcontainers with Spring Boot 3.5
  • What is the difference between @DataJpaTest and @SpringBootTest?
  • How to mock dependencies in a Spring service test?
  • Explain slice testing patterns in Spring Boot applications

Pro Tips

  • 💡Prioritize fast feedback: Start with isolated unit tests for core logic, then layer on integration and slice tests only where necessary to validate component interactions.
  • 💡Leverage `@ServiceConnection` or Testcontainers' native integration for external services to ensure tests run against realistic environments without manual setup overhead.
  • 💡Implement a robust test data management strategy. Use builders, factories, or database cleaning techniques (like `spring.jpa.hibernate.ddl-auto=create-drop` or specialized libraries) to ensure test isolation and reproducibility.

What this skill does

  • Configures slice-based testing to isolate controller, repository, and JSON layers
  • Integrates Testcontainers for ephemeral, real-database testing with @ServiceConnection
  • Implements standard Mockito patterns for dependency isolation in unit tests
  • Optimizes test performance by managing Spring context caching
  • Standardizes annotation usage for specific Spring Boot application contexts

When not to use it

  • Testing frontend code that does not communicate with a Spring backend
  • End-to-end browser automation tasks that fall outside of server-side logic validation
  • Simple Java utilities that do not interact with the Spring container or database

Example workflow

  1. Define unit tests for business logic using standard JUnit and Mockito
  2. Apply @DataJpaTest to verify repository query methods against a test-configured container
  3. Configure Testcontainers with @ServiceConnection to manage database connectivity
  4. Construct @WebMvcTest classes to validate controller routing and input validation
  5. Verify test execution times to ensure context caching is effectively reducing overhead

Prerequisites

  • JUnit 5
  • Mockito
  • Spring Boot 3.x+ project structure
  • Docker desktop or environment compatible with Testcontainers

Pitfalls & limitations

  • !Over-reliance on @SpringBootTest causing slow build times due to full context loading
  • !Leaking test state between database integration tests
  • !Misconfiguring Testcontainers causing container initialization timeouts in CI

FAQ

Why is @SpringBootTest considered slow?
It initializes the entire application context, including all beans, data sources, and configurations, which consumes significant memory and time compared to targeted slice tests.
Does this skill support non-relational databases?
Yes, Testcontainers supports various module types for NoSQL databases, though the examples focus on PostgreSQL as the primary template.
What is the primary benefit of @ServiceConnection?
It automates the wiring of Testcontainer-managed services to Spring Boot properties, removing the need for manually setting dynamic connection strings.

How it compares

Unlike generic testing prompts that ignore framework-specific constraints, this skill leverages Spring's built-in testing slice annotations to prevent common context-loading performance bottlenecks.

Source & trust

282 stars📄 MIT🕒 Updated 2026-06-15
📄 Full skill instructions — original source: giuseppe-trisciuoglio/developer-kit
# Spring Boot Testing Patterns

## Overview

This skill provides comprehensive guidance for writing robust test suites for Spring Boot applications. It covers unit testing with Mockito, integration testing with Testcontainers, performance-optimized slice testing patterns, and best practices for maintaining fast feedback loops.

## When to Use This Skill

Use this skill when:
- Writing unit tests for services, repositories, or utilities
- Implementing integration tests with real databases using Testcontainers
- Setting up performance-optimized test slices (@DataJpaTest, @WebMvcTest)
- Configuring Spring Boot 3.5+ @ServiceConnection for container management
- Testing REST APIs with MockMvc, TestRestTemplate, or WebTestClient
- Optimizing test performance through context caching and container reuse
- Setting up CI/CD pipelines for integration tests
- Implementing comprehensive test strategies for monolithic or microservices applications

## Core Concepts

### Test Architecture Philosophy

Spring Boot testing follows a layered approach with distinct test types:

**1. Unit Tests**
- Fast, isolated tests without Spring context
- Use Mockito for dependency injection
- Focus on business logic validation
- Target completion time: < 50ms per test

**2. Slice Tests**
- Minimal Spring context loading for specific layers
- Use @DataJpaTest for repository tests
- Use @WebMvcTest for controller tests
- Use @WebFluxTest for reactive controller tests
- Target completion time: < 100ms per test

**3. Integration Tests**
- Full Spring context with real dependencies
- Use @SpringBootTest with @ServiceConnection containers
- Test complete application flows
- Target completion time: < 500ms per test

### Key Testing Annotations

**Spring Boot Test Annotations:**
- @SpringBootTest: Load full application context (use sparingly)
- @DataJpaTest: Load only JPA components (repositories, entities)
- @WebMvcTest: Load only MVC layer (controllers, @ControllerAdvice)
- @WebFluxTest: Load only WebFlux layer (reactive controllers)
- @JsonTest: Load only JSON serialization components

**Testcontainer Annotations:**
- @ServiceConnection: Wire Testcontainer to Spring Boot test (Spring Boot 3.5+)
- @DynamicPropertySource: Register dynamic properties at runtime
- @Testcontainers: Enable Testcontainers lifecycle management

## Dependencies

### Maven Dependencies

<dependencies>
<!-- Spring Boot Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- Testcontainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>

<!-- Additional Testing Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>


### Gradle Dependencies

dependencies {
// Spring Boot Test Starter
testImplementation("org.springframework.boot:spring-boot-starter-test")

// Testcontainers
testImplementation("org.testcontainers:junit-jupiter:1.19.0")
testImplementation("org.testcontainers:postgresql:1.19.0")

// Additional Dependencies
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
}


## Instructions

### Unit Testing Pattern

Test business logic with mocked dependencies:

class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

@Test
void shouldFindUserByIdWhenExists() {
// Arrange
Long userId = 1L;
User user = new User();
user.setId(userId);
user.setEmail("[email protected]");

when(userRepository.findById(userId)).thenReturn(Optional.of(user));

// Act
Optional<User> result = userService.findById(userId);

// Assert
assertThat(result).isPresent();
assertThat(result.get().getEmail()).isEqualTo("[email protected]");
verify(userRepository, times(1)).findById(userId);
}
}


### Slice Testing Pattern

Use focused test slices for specific layers:

// Repository test with minimal context
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
public class UserRepositoryIntegrationTest {

@Autowired
private UserRepository userRepository;

@Test
void shouldSaveAndRetrieveUserFromDatabase() {
// Arrange
User user = new User();
user.setEmail("[email protected]");
user.setName("Test User");

// Act
User saved = userRepository.save(user);
userRepository.flush();

Optional<User> retrieved = userRepository.findByEmail("[email protected]");

// Assert
assertThat(retrieved).isPresent();
assertThat(retrieved.get().getName()).isEqualTo("Test User");
}
}


### REST API Testing Pattern

Test controllers with MockMvc for faster execution:

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class UserControllerIntegrationTest {

@Autowired
private MockMvc mockMvc;

@Autowired
private ObjectMapper objectMapper;

@Autowired
private UserService userService;

@Test
void shouldCreateUserAndReturn201() throws Exception {
User user = new User();
user.setEmail("[email protected]");
user.setName("New User");

mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.email").value("[email protected]"))
.andExpect(jsonPath("$.name").value("New User"));
}
}


### Testcontainers with @ServiceConnection

Configure containers with Spring Boot 3.5+:

@TestConfiguration
public class TestContainerConfig {

@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgresContainer() {
return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
}
}


## Examples

### Basic Unit Test

@Test
void shouldCalculateTotalPrice() {
// Arrange
OrderItem item1 = new OrderItem();
item1.setPrice(10.0);
item1.setQuantity(2);

OrderItem item2 = new OrderItem();
item2.setPrice(15.0);
item2.setQuantity(1);

List<OrderItem> items = List.of(item1, item2);

// Act
double total = orderService.calculateTotal(items);

// Assert
assertThat(total).isEqualTo(35.0);
}


### Integration Test with Testcontainers

@SpringBootTest
@TestContainerConfig
public class OrderServiceIntegrationTest {

@Autowired
private OrderService orderService;

@Autowired
private UserRepository userRepository;

@MockBean
private PaymentService paymentService;

@Test
void shouldCreateOrderWithRealDatabase() {
// Arrange
User user = new User();
user.setEmail("[email protected]");
user.setName("John Doe");
User savedUser = userRepository.save(user);

OrderRequest request = new OrderRequest();
request.setUserId(savedUser.getId());
request.setItems(List.of(
new OrderItemRequest(1L, 2),
new OrderItemRequest(2L, 1)
));

when(paymentService.processPayment(any())).thenReturn(true);

// Act
OrderResponse response = orderService.createOrder(request);

// Assert
assertThat(response.getOrderId()).isNotNull();
assertThat(response.getStatus()).isEqualTo("COMPLETED");
verify(paymentService, times(1)).processPayment(any());
}
}


### Reactive Test Pattern

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class ReactiveUserControllerIntegrationTest {

@Autowired
private WebTestClient webTestClient;

@Test
void shouldReturnUserAsJsonReactive() {
// Arrange
User user = new User();
user.setEmail("[email protected]");
user.setName("Reactive User");

// Act & Assert
webTestClient.get()
.uri("/api/users/1")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.email").isEqualTo("[email protected]")
.jsonPath("$.name").isEqualTo("Reactive User");
}
}


## Best Practices

### 1. Choose the Right Test Type

Select appropriate test annotations based on scope:

// Use @DataJpaTest for repository-only tests (fastest)
@DataJpaTest
public class UserRepositoryTest { }

// Use @WebMvcTest for controller-only tests
@WebMvcTest(UserController.class)
public class UserControllerTest { }

// Use @SpringBootTest only for full integration testing
@SpringBootTest
public class UserServiceFullIntegrationTest { }


### 2. Use @ServiceConnection for Container Management

Prefer @ServiceConnection over manual @DynamicPropertySource for cleaner code:

// Good - Spring Boot 3.5+
@TestConfiguration
public class TestConfig {
@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgres() {
return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"));
}
}


### 3. Keep Tests Deterministic

Always initialize test data explicitly:

// Good - Explicit setup
@BeforeEach
void setUp() {
userRepository.deleteAll();
User user = new User();
user.setEmail("[email protected]");
userRepository.save(user);
}

// Avoid - Depending on other tests
@Test
void testUserExists() {
// Assumes previous test created a user
Optional<User> user = userRepository.findByEmail("[email protected]");
assertThat(user).isPresent();
}


### 4. Use Meaningful Assertions

Leverage AssertJ for readable, fluent assertions:

// Good - Clear, readable assertions
assertThat(user.getEmail())
.isEqualTo("[email protected]");

assertThat(users)
.hasSize(3)
.contains(expectedUser);

// Avoid - JUnit assertions
assertEquals("[email protected]", user.getEmail());
assertTrue(users.size() == 3);


### 5. Organize Tests by Layer

Group related tests in separate classes to optimize context caching:

// Repository tests (uses @DataJpaTest)
public class UserRepositoryTest { }

// Controller tests (uses @WebMvcTest)
public class UserControllerTest { }

// Service tests (uses mocks, no context)
public class UserServiceTest { }

// Full integration tests (uses @SpringBootTest)
public class UserFullIntegrationTest { }


## Performance Optimization

### Context Caching Strategy

Maximize Spring context caching by grouping tests with similar configurations:

// Group repository tests with same configuration
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
@TestPropertySource(properties = "spring.datasource.url=jdbc:postgresql:testdb")
public class UserRepositoryTest { }

// Group controller tests with same configuration
@WebMvcTest(UserController.class)
@AutoConfigureMockMvc
public class UserControllerTest { }


### Container Reuse Strategy

Reuse Testcontainers at JVM level for better performance:

@Testcontainers
public class ContainerConfig {
static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
DockerImageName.parse("postgres:16-alpine"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");

@BeforeAll
static void startAll() {
POSTGRES.start();
}

@AfterAll
static void stopAll() {
POSTGRES.stop();
}
}


## Test Execution

### Maven Test Execution

# Run all tests
./mvnw test

# Run specific test class
./mvnw test -Dtest=UserServiceTest

# Run integration tests only
./mvnw test -Dintegration-test=true

# Run tests with coverage
./mvnw clean jacoco:prepare-agent test jacoco:report


### Gradle Test Execution

# Run all tests
./gradlew test

# Run specific test class
./gradlew test --tests UserServiceTest

# Run integration tests only
./gradlew integrationTest

# Run tests with coverage
./gradlew test jacocoTestReport


## CI/CD Configuration

### GitHub Actions Example

name: Spring Boot Tests

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: test
POSTGRES_USER: test
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v3

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-

- name: Run tests
run: ./mvnw test -Dspring.profiles.active=test


### Docker Compose for Local Testing

version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
postgres_data:


## References

For detailed information, refer to the following resources:

- [API Reference](./references/api-reference.md) - Complete test annotations and utilities
- [Best Practices](./references/best-practices.md) - Testing patterns and optimization
- [Workflow Patterns](./references/workflow-patterns.md) - Complete integration test examples

## Related Skills

- **spring-boot-dependency-injection** - Unit testing patterns with constructor injection
- **spring-boot-rest-api-standards** - REST API patterns to test
- **spring-boot-crud-patterns** - CRUD patterns to test
- **unit-test-service-layer** - Advanced service layer testing techniques

## Performance Targets

- **Unit tests**: < 50ms per test
- **Slice tests**: < 100ms per test
- **Integration tests**: < 500ms per test
- **Maximize context caching** by grouping tests with same configuration
- **Reuse Testcontainers** at JVM level where possible

## Key Principles

1. Use test slices for focused, fast tests
2. Prefer @ServiceConnection on Spring Boot 3.5+
3. Keep tests deterministic with explicit setup
4. Mock external dependencies, use real databases
5. Avoid @DirtiesContext unless absolutely necessary
6. Organize tests by layer to optimize context reuse

This skill enables building comprehensive test suites that validate Spring Boot applications reliably while maintaining fast feedback loops for development.

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/spring-boot-test-patterns/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/giuseppe-trisciuoglio/developer-kit/spring-boot-test-patterns/SKILL.md
  • Cursor: ~/.cursor/skills/giuseppe-trisciuoglio/developer-kit/spring-boot-test-patterns/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/giuseppe-trisciuoglio/developer-kit/spring-boot-test-patterns/SKILL.md

🚀 Install with CLI:
npx skills add giuseppe-trisciuoglio/developer-kit

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid testing & quality assurance issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under Testing & Quality Assurance and is published by Giuseppe Trisciuoglio, maintained in giuseppe-trisciuoglio/developer-kit.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.