
Spring Boot Test Patterns
- 2k installs
- 311 repo stars
- Updated June 22, 2026
- giuseppe-trisciuoglio/developer-kit
spring-boot-test-patterns is an agent skill that Provides comprehensive testing patterns for Spring Boot applications covering unit, integration, slice, and container-based testing with JUnit 5, Mockito, Testc.
About
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 name spring-boot-test-patterns description 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 allowed-tools Read Write Edit Bash Glob Grep Spring Boot Testing Patterns Overview Comprehensive guidance for writing robust test suites for Spring Boot applications using JUnit 5 Mockito Testcontainers and performance-optimized slice testing patterns When to Use Writing unit tests for services or repositories with mocked dependencies Implementing integration tests with real databases via Testcontainers Testing REST APIs with WebMvcTest or MockMvc Configuring ServiceConnection for container management in Spring Boot 3 5 Quick Reference Test Type.
- Spring Boot Testing Patterns
- Writing unit tests for services or repositories with mocked dependencies
- Implementing integration tests with real databases via Testcontainers
- Testing REST APIs with `@WebMvcTest` or MockMvc
- Configuring `@ServiceConnection` for container management in Spring Boot 3.5+
Spring Boot Test Patterns by the numbers
- 1,990 all-time installs (skills.sh)
- +63 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #173 of 1,041 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
spring-boot-test-patterns capabilities & compatibility
- Capabilities
- spring boot testing patterns · writing unit tests for services or repositories · implementing integration tests with real databas · testing rest apis with `@webmvctest` or mockmvc · configuring `@serviceconnection` for container m
- Use cases
- documentation
What spring-boot-test-patterns says it does
Use when writing tests, @Test methods, @MockBean mocks, or implementing test suites for Spring Boot applications.
**Unit Tests** — Fast, isolated tests without Spring context (< 50ms) 2.
**Slice Tests** — Minimal Spring context for specific layers (< 100ms) 3.
See [testcontainers-setup.md](references/testcontainers-setup.md) for detailed configuration.
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill spring-boot-test-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2k |
|---|---|
| repo stars | ★ 311 |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 22, 2026 |
| Repository | giuseppe-trisciuoglio/developer-kit ↗ |
What problem does spring-boot-test-patterns solve for developers using this skill?
Provides comprehensive testing patterns for Spring Boot applications covering unit, integration, slice, and container-based testing with JUnit 5, Mockito, Testcontainers, and performance optimization.
Who is it for?
Developers who need spring-boot-test-patterns patterns described in the cached skill documentation.
Skip if: Skip when docs are empty or the task is outside the skill's documented scope.
When should I use this skill?
Provides comprehensive testing patterns for Spring Boot applications covering unit, integration, slice, and container-based testing with JUnit 5, Mockito, Testcontainers, and performance optimization.
What you get
Actionable workflows and conventions from SKILL.md for spring-boot-test-patterns.
- JUnit 5 test classes
- Testcontainers configurations
Files
Spring Boot Testing Patterns
Overview
Comprehensive guidance for writing robust test suites for Spring Boot applications using JUnit 5, Mockito, Testcontainers, and performance-optimized slice testing patterns.
When to Use
- Writing unit tests for services or repositories with mocked dependencies
- Implementing integration tests with real databases via Testcontainers
- Testing REST APIs with
@WebMvcTestor MockMvc - Configuring
@ServiceConnectionfor container management in Spring Boot 3.5+
Quick Reference
| 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 |
Core Concepts
Test Architecture Philosophy
1. Unit Tests — Fast, isolated tests without Spring context (< 50ms) 2. Slice Tests — Minimal Spring context for specific layers (< 100ms) 3. Integration Tests — Full Spring context with real dependencies (< 500ms)
Key Annotations
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 only
Testcontainers:
@ServiceConnection— Wire Testcontainer to Spring Boot (3.5+)@DynamicPropertySource— Register dynamic properties at runtime@Testcontainers— Enable Testcontainers lifecycle management
Instructions
1. Unit Testing Pattern
Test business logic with mocked dependencies:
@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.
2. Slice Testing Pattern
Use focused test slices for specific layers:
@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.
3. REST API Testing Pattern
Test controllers with MockMvc:
@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"));
}
}4. Testcontainers with @ServiceConnection
Configure containers with Spring Boot 3.5+:
@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.
5. Add Dependencies
Include required testing dependencies:
<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.
6. Configure CI/CD
Set up GitHub Actions for automated testing:
name: 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 testSee ci-cd-configuration.md for full CI/CD patterns.
Validation Checkpoints
After implementing tests, verify:
- Container running:
docker ps(look for testcontainer images) - Context loaded: check startup logs for "Started Application in X.XX seconds"
- Test isolation: run tests individually and confirm no cross-contamination
Examples
Full Integration Test with @ServiceConnection
@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 Database
@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.
Best Practices
- Use the right test type:
@DataJpaTestfor repositories,@WebMvcTestfor controllers,@SpringBootTestonly for full integration - Prefer `@ServiceConnection` on Spring Boot 3.5+ for cleaner container management over
@DynamicPropertySource - Keep tests deterministic: Initialize all test data explicitly in
@BeforeEach - Organize by layer: Group tests by layer to maximize context caching
- Reuse Testcontainers at JVM level (
withReuse(true)+TESTCONTAINERS_REUSE_ENABLE=true) - Avoid `@DirtiesContext`: Forces context rebuild, significantly hurts performance
- Mock external services, use real databases only when necessary
- Performance targets: Unit < 50ms, Slice < 100ms, Integration < 500ms
Constraints and Warnings
- Never use
@DirtiesContextunless absolutely necessary (forces context rebuild) - Avoid mixing
@MockBeanwith different configurations (creates separate contexts) - Testcontainers require Docker; ensure CI/CD pipelines have Docker support
- Do not rely on test execution order; each test must be independent
- Be cautious with
@TestPropertySource(creates separate contexts) - Do not use
@SpringBootTestfor unit tests; use plain Mockito instead - Context caching can be invalidated by different
@MockBeanconfigurations - Avoid static mutable state in tests (causes flaky tests)
References
- [test-dependencies.md](references/test-dependencies.md) — Maven/Gradle test dependencies
- [unit-testing.md](references/unit-testing.md) — Unit testing with Mockito patterns
- [slice-testing.md](references/slice-testing.md) — Repository, controller, and JSON slice tests
- [testcontainers-setup.md](references/testcontainers-setup.md) — Testcontainers configuration patterns
- [ci-cd-configuration.md](references/ci-cd-configuration.md) — GitHub Actions, GitLab CI, Docker Compose
- [api-reference.md](references/api-reference.md) — Complete test annotations and utilities
- [best-practices.md](references/best-practices.md) — Testing patterns and optimization
- [workflow-patterns.md](references/workflow-patterns.md) — Complete integration test examples
Spring Boot Test API Reference
Test Annotations
Spring Boot Test Annotations:
@SpringBootTest: Load full application context (use sparingly)@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT): Full test with random HTTP port@SpringBootTest(webEnvironment = WebEnvironment.MOCK): Full test with mock web environment@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@RestClientTest: Load only REST client components@AutoConfigureMockMvc: Provide MockMvc bean in@SpringBootTest@AutoConfigureWebTestClient: Provide WebTestClient bean for WebFlux tests@AutoConfigureTestDatabase: Control test database configuration
Testcontainer Annotations:
@ServiceConnection: Wire Testcontainer to Spring Boot test (Spring Boot 3.5+)@DynamicPropertySource: Register dynamic properties at runtime@Container: Mark field as Testcontainer (requires@Testcontainers)@Testcontainers: Enable Testcontainers lifecycle management
Test Lifecycle Annotations:
@BeforeEach: Run before each test method@AfterEach: Run after each test method@BeforeAll: Run once before all tests in class (must be static)@AfterAll: Run once after all tests in class (must be static)@DisplayName: Custom test name for reports@Disabled: Skip test@Tag: Tag tests for selective execution
Test Isolation Annotations:
@DirtiesContext: Clear Spring context after test (forces rebuild)@DirtiesContext(classMode = ClassMode.AFTER_CLASS): Clear after entire class
Common Test Utilities
MockMvc Methods:
mockMvc.perform(get("/path")): Perform GET requestmockMvc.perform(post("/path")).contentType(MediaType.APPLICATION_JSON): POST with content type.andExpect(status().isOk()): Assert HTTP status.andExpect(content().contentType("application/json")): Assert content type.andExpect(jsonPath("$.field").value("expected")): Assert JSON path value
TestRestTemplate Methods:
restTemplate.getForEntity("/path", String.class): GET requestrestTemplate.postForEntity("/path", body, String.class): POST requestresponse.getStatusCode(): Get HTTP statusresponse.getBody(): Get response body
WebTestClient Methods (Reactive):
webTestClient.get().uri("/path").exchange(): Perform GET request.expectStatus().isOk(): Assert status.expectBody().jsonPath("$.field").isEqualTo(value): Assert JSON
Test Slices Performance Guidelines
- Unit tests: Complete in <50ms each
- Integration tests: Complete in <500ms each
- Maximize context caching by grouping tests with same configuration
- Reuse Testcontainers at JVM level where possible
Common Test Annotations Reference
| Annotation | Purpose | When to Use |
|---|---|---|
@SpringBootTest | Full application context | Full integration tests only |
@DataJpaTest | JPA components only | Repository and entity tests |
@WebMvcTest | MVC layer only | Controller tests |
@WebFluxTest | WebFlux layer only | Reactive controller tests |
@ServiceConnection | Container integration | Spring Boot 3.5+ with Testcontainers |
@DynamicPropertySource | Dynamic properties | Pre-3.5 or custom configuration |
@DirtiesContext | Context cleanup | When absolutely necessary |
Spring Boot Testing Best Practices
Choose the Right Test Type
Select the most efficient test annotation for your use case:
// 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 { }Use @ServiceConnection for Container Management (Spring Boot 3.5+)
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"));
}
}
// Avoid - Manual property registration
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
// ... more properties
}Keep Tests Deterministic
Always initialize test data explicitly and never depend on test execution order:
// Good - Explicit setup
@BeforeEach
void setUp() {
userRepository.deleteAll();
User user = new User();
user.setEmail("test@example.com");
userRepository.save(user);
}
// Avoid - Depending on other tests
@Test
void testUserExists() {
// Assumes previous test created a user
Optional<User> user = userRepository.findByEmail("test@example.com");
assertThat(user).isPresent();
}Use Transactional Tests Carefully
Mark test classes with @Transactional for automatic rollback, but understand the implications:
@SpringBootTest
@Transactional // Automatically rolls back after each test
public class UserControllerIntegrationTest {
@Test
void shouldCreateUser() throws Exception {
// Changes will be rolled back after test
mockMvc.perform(post("/api/users")....)
.andExpect(status().isCreated());
}
}Note: Be aware that @Transactional test behavior may differ from production due to lazy loading and flush semantics.
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 { }Use Meaningful Assertions
Leverage AssertJ for readable, fluent assertions:
// Good - Clear, readable assertions
assertThat(user.getEmail())
.isEqualTo("test@example.com");
assertThat(users)
.hasSize(3)
.contains(expectedUser);
assertThatThrownBy(() -> userService.save(invalidUser))
.isInstanceOf(ValidationException.class)
.hasMessageContaining("Email is required");
// Avoid - JUnit assertions
assertEquals("test@example.com", user.getEmail());
assertTrue(users.size() == 3);Mock External Dependencies
Mock external services but use real databases for integration tests:
// Good - Mock external services, use real DB
@SpringBootTest
@TestContainerConfig.class
public class OrderServiceTest {
@MockBean
private EmailService emailService;
@Autowired
private OrderRepository orderRepository;
@Test
void shouldSendConfirmationEmail() {
// Use real database, mock email service
Order order = new Order();
orderService.createOrder(order);
verify(emailService, times(1)).sendConfirmation(order);
}
}
// Avoid - Mocking the database layer
@Test
void shouldCreateOrder() {
when(orderRepository.save(any())).thenReturn(mockOrder);
// Tests don't verify actual database behavior
}Use Test Fixtures for Common Data
Create reusable test data builders:
public class UserTestFixture {
public static User validUser() {
User user = new User();
user.setEmail("test@example.com");
user.setName("Test User");
return user;
}
public static User userWithEmail(String email) {
User user = validUser();
user.setEmail(email);
return user;
}
}
// Usage in tests
@Test
void shouldSaveUser() {
User user = UserTestFixture.validUser();
userRepository.save(user);
assertThat(userRepository.count()).isEqualTo(1);
}Document Complex Test Scenarios
Use @DisplayName and comments for complex test logic:
@Test
@DisplayName("Should validate email format and reject duplicates with proper error message")
void shouldValidateEmailBeforePersisting() {
// Given: Two users with the same email
User user1 = new User();
user1.setEmail("test@example.com");
userRepository.save(user1);
User user2 = new User();
user2.setEmail("test@example.com"); // Duplicate email
// When: Attempting to save duplicate
// Then: Should throw exception with clear message
assertThatThrownBy(() -> {
userRepository.save(user2);
userRepository.flush();
})
.isInstanceOf(DataIntegrityViolationException.class)
.hasMessageContaining("unique constraint");
}Avoid Common Pitfalls
// Avoid: Using @DirtiesContext without reason (forces context rebuild)
@SpringBootTest
@DirtiesContext // DON'T USE unless absolutely necessary
public class ProblematicTest { }
// Avoid: Mixing multiple profiles in same test suite
@SpringBootTest(properties = "spring.profiles.active=dev,test,prod")
public class MultiProfileTest { }
// Avoid: Starting containers manually
@SpringBootTest
public class ManualContainerTest {
static {
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();
postgres.start(); // Avoid - use @ServiceConnection instead
}
}
// Good: Consistent configuration, minimal context switching
@SpringBootTest
@TestContainerConfig
public class ProperTest { }Test Naming Conventions
Convention: Use descriptive method names that start with should or test to make test intent explicit.
Naming Rules:
- Prefix: Start with
shouldortestto clearly indicate test purpose - Structure: Use camelCase for readability (no underscores)
- Clarity: Name should indicate what is being tested and the expected outcome
- Example pattern:
should[ExpectedBehavior]When[Condition]()
Examples:
shouldReturnUsersJson()
shouldThrowNotFoundWhenIdDoesntExist()
shouldPropagateExceptionOnPersistenceError()
shouldSaveAndRetrieveUserFromDatabase()
shouldValidateEmailFormatBeforePersisting()Apply these rules consistently across all integration test methods.
CI/CD Configuration
GitHub Actions
Basic Test Workflow
name: Spring Boot Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
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
- name: Generate test report
uses: dorny/test-reporter@v1
if: always()
with:
name: Maven Tests
path: target/surefire-reports/*.xml
reporter: java-junitWith Testcontainers
name: Tests with Testcontainers
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: Run tests
run: ./mvnw test
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
SPRING_DATASOURCE_USERNAME: test
SPRING_DATASOURCE_PASSWORD: testGitLab CI
stages:
- test
test:
stage: test
image: openjdk:17-jdk-slim
services:
- name: postgres:16-alpine
alias: postgres
variables:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
variables:
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/testdb"
SPRING_DATASOURCE_USERNAME: test
SPRING_DATASOURCE_PASSWORD: test
cache:
paths:
- .m2/repository/
script:
- ./mvnw test
artifacts:
when: always
reports:
junit: target/surefire-reports/TEST-*.xmlDocker 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
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: testdb
MYSQL_USER: test
MYSQL_PASSWORD: test
MYSQL_ROOT_PASSWORD: test
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
mysql_data:Run tests with: docker-compose up -d && ./mvnw test
Maven Test Profiles
<profiles>
<profile>
<id>unit-tests</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>Run with: ./mvnw test -Punit-tests or ./mvnw verify -Pintegration-tests
Slice Testing Patterns
Repository Slice Tests
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveAndRetrieveUser() {
// Arrange
User user = new User();
user.setEmail("test@example.com");
user.setName("Test User");
// Act
User saved = userRepository.save(user);
userRepository.flush();
// Assert
Optional<User> retrieved = userRepository.findByEmail("test@example.com");
assertThat(retrieved).isPresent();
assertThat(retrieved.get().getName()).isEqualTo("Test User");
}
@Test
void shouldFindAllActiveUsers() {
// Arrange
User activeUser = new User();
activeUser.setEmail("active@example.com");
activeUser.setActive(true);
User inactiveUser = new User();
inactiveUser.setEmail("inactive@example.com");
inactiveUser.setActive(false);
userRepository.saveAll(List.of(activeUser, inactiveUser));
// Act
List<User> activeUsers = userRepository.findByActiveTrue();
// Assert
assertThat(activeUsers).hasSize(1);
assertThat(activeUsers.get(0).getEmail()).isEqualTo("active@example.com");
}
@Test
void shouldDeleteUser() {
// Arrange
User user = new User();
user.setEmail("delete@example.com");
User saved = userRepository.save(user);
// Act
userRepository.deleteById(saved.getId());
userRepository.flush();
// Assert
assertThat(userRepository.findById(saved.getId())).isEmpty();
}
}Controller Slice Tests
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private UserService userService;
@Test
void shouldGetUserById() throws Exception {
// Arrange
User user = new User();
user.setId(1L);
user.setEmail("test@example.com");
user.setName("Test User");
when(userService.findById(1L)).thenReturn(Optional.of(user));
// Act & Assert
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.email").value("test@example.com"))
.andExpect(jsonPath("$.name").value("Test User"));
}
@Test
void shouldReturn404WhenUserNotFound() throws Exception {
// Arrange
when(userService.findById(999L)).thenReturn(Optional.empty());
// Act & Assert
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound());
}
@Test
void shouldCreateUser() throws Exception {
// Arrange
CreateUserRequest request = new CreateUserRequest();
request.setEmail("new@example.com");
request.setName("New User");
User createdUser = new User();
createdUser.setId(1L);
createdUser.setEmail("new@example.com");
createdUser.setName("New User");
when(userService.createUser(any())).thenReturn(createdUser);
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.email").value("new@example.com"));
}
@Test
void shouldValidateRequest() throws Exception {
// Arrange
CreateUserRequest request = new CreateUserRequest();
request.setEmail(""); // Invalid
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest());
}
}JSON Slice Tests
@JsonTest
class UserJsonSerializationTest {
@Autowired
private JacksonTester<User> json;
@Test
void shouldSerializeUser() throws JsonProcessingException {
// Arrange
User user = new User();
user.setId(1L);
user.setEmail("test@example.com");
user.setName("Test User");
// Act
JsonContent<User> result = json.write(user);
// Assert
assertThat(result).hasJsonPathValue("$.id", 1);
assertThat(result).hasJsonPathValue("$.email", "test@example.com");
assertThat(result).hasJsonPathValue("$.name", "Test User");
}
@Test
void shouldDeserializeUser() throws IOException {
// Arrange
String jsonContent = """
{
"id": 1,
"email": "test@example.com",
"name": "Test User"
}
""";
// Act
User result = json.parse(jsonContent).getObject();
// Assert
assertThat(result.getId()).isEqualTo(1L);
assertThat(result.getEmail()).isEqualTo("test@example.com");
assertThat(result.getName()).isEqualTo("Test User");
}
}WebFlux Controller Tests
@WebFluxTest(UserController.class)
class ReactiveUserControllerTest {
@Autowired
private WebTestClient webTestClient;
@MockBean
private UserService userService;
@Test
void shouldGetUserById() {
// Arrange
User user = new User();
user.setId(1L);
user.setEmail("test@example.com");
when(userService.findById(1L)).thenReturn(Mono.just(user));
// Act & Assert
webTestClient.get()
.uri("/api/users/1")
.exchange()
.expectStatus().isOk()
.expectBody(User.class)
.isEqualTo(user);
}
@Test
void shouldReturn404WhenUserNotFound() {
// Arrange
when(userService.findById(999L)).thenReturn(Mono.empty());
// Act & Assert
webTestClient.get()
.uri("/api/users/999")
.exchange()
.expectStatus().isNotFound();
}
}Testing ControllerAdvice
@WebMvcTest(UserController.class)
class UserControllerExceptionTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldHandleUserNotFoundException() throws Exception {
// Arrange
when(userService.findById(999L))
.thenThrow(new UserNotFoundException("User not found"));
// Act & Assert
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.message").value("User not found"));
}
}Test Dependencies Setup
Maven Dependencies
Basic Testing Setup
<dependencies>
<!-- Spring Boot Test Starter (includes JUnit 5, Mockito, AssertJ) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Testcontainers Core -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<!-- PostgreSQL Testcontainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<!-- MySQL Testcontainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<!-- Additional 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")
}Version Selection
- Spring Boot 3.x: Use Testcontainers 1.19.x+
- Spring Boot 2.x: Use Testcontainers 1.17.x
- Always check Testcontainers Documentation for latest versions
Optional Testing Dependencies
H2 In-Memory Database
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>WireMock for HTTP Mocking
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>3.5.2</version>
<scope>test</scope>
</dependency>Awaitility for Async Testing
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>Testcontainers Configuration
Spring Boot 3.5+ @ServiceConnection
@TestConfiguration
public class TestContainerConfig {
@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgresContainer() {
return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
}
@Bean
@ServiceConnection
public GenericContainer<?> redisContainer() {
return new GenericContainer<>(DockerImageName.parse("redis:7-alpine"))
.withExposedPorts(6379);
}
}Apply with @Import(TestContainerConfig.class) on test classes.
Traditional @DynamicPropertySource
@Testcontainers
class UserServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
DockerImageName.parse("postgres:16-alpine"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
}Multiple Containers
@Testcontainers
class MultiContainerIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
"postgres:16-alpine")
.withDatabaseName("testdb");
@Container
static GenericContainer<?> redis = new GenericContainer<>(
"redis:7-alpine")
.withExposedPorts(6379);
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.redis.host", redis::getHost);
registry.add("spring.redis.port", redis::getFirstMappedPort);
}
}Container Reuse Strategy
@Testcontainers(disableWithoutDocker = true)
class ContainerConfig {
static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
DockerImageName.parse("postgres:16-alpine"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test")
.withReuse(true);
@BeforeAll
static void startAll() {
POSTGRES.start();
}
@AfterAll
static void stopAll() {
POSTGRES.stop();
}
}Enable reuse with environment variable: TESTCONTAINERS_REUSE_ENABLE=true
MySQL Container
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("mysql:8.0"))
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");MongoDB Container
@Container
static MongoDBContainer<?> mongodb = new MongoDBContainer<>(
DockerImageName.parse("mongo:6.0"))
.withExposedPorts(27017);Kafka Container
@Container
static KafkaContainer kafka = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka:7.5.0"));
@DynamicPropertySource
static void kafkaProperties(DynamicPropertyRegistry registry) {
registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers);
}Container Initialization
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
"postgres:16-alpine")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test")
.withInitScript("sql/init-test.sql") // Run init script
.withCommand("postgres", "-c", "max_connections=200"); // Custom configNetwork Configuration
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
"postgres:16-alpine")
.withNetwork(Network.SHARED)
.withNetworkAliases("pgdb"); // Access via hostnameUnit Testing Patterns
Basic Unit Test with Mockito
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void shouldFindUserByIdWhenExists() {
// Arrange
Long userId = 1L;
User user = new User();
user.setId(userId);
user.setEmail("test@example.com");
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
// Act
Optional<User> result = userService.findById(userId);
// Assert
assertThat(result).isPresent();
assertThat(result.get().getEmail()).isEqualTo("test@example.com");
verify(userRepository, times(1)).findById(userId);
}
@Test
void shouldReturnEmptyWhenUserNotFound() {
// Arrange
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
// Act
Optional<User> result = userService.findById(userId);
// Assert
assertThat(result).isEmpty();
verify(userRepository, times(1)).findById(userId);
}
@Test
void shouldThrowExceptionWhenCreatingUserWithInvalidEmail() {
// Arrange
CreateUserRequest request = new CreateUserRequest();
request.setEmail("invalid-email");
request.setName("Test User");
// Act & Assert
assertThatThrownBy(() -> userService.createUser(request))
.isInstanceOf(InvalidEmailException.class)
.hasMessage("Invalid email format");
verify(userRepository, never()).save(any());
}
}Testing Business Logic
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private ProductService productService;
@InjectMocks
private OrderService orderService;
@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);
}
@Test
void shouldApplyDiscountForLargeOrders() {
// Arrange
Order order = new Order();
order.setTotal(1000.0);
// Act
orderService.applyDiscount(order, 10);
// Assert
assertThat(order.getTotal()).isEqualTo(900.0);
}
}Testing Exception Scenarios
@Test
void shouldThrowExceptionWhenInsufficientStock() {
// Arrange
OrderRequest request = new OrderRequest();
request.setProductId(1L);
request.setQuantity(100);
when(productService.getStock(1L)).thenReturn(50);
// Act & Assert
assertThatThrownBy(() -> orderService.createOrder(request))
.isInstanceOf(InsufficientStockException.class)
.hasMessageContaining("Insufficient stock");
}Parameterized Tests
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
class ParameterizedUserServiceTest {
@ParameterizedTest
@ValueSource(strings = {"user@example.com", "test@test.com", "admin@domain.com"})
void shouldAcceptValidEmails(String email) {
assertThat(userService.isValidEmail(email)).isTrue();
}
@ParameterizedTest
@CsvSource({
"10, 2, 20",
"5, 3, 15",
"100, 0, 0"
})
void shouldCalculateTotalCorrectly(double price, int quantity, double expectedTotal) {
assertThat(orderService.calculateTotal(price, quantity))
.isEqualTo(expectedTotal);
}
@ParameterizedTest
@MethodSource("provideInvalidEmails")
void shouldRejectInvalidEmails(String email) {
assertThat(userService.isValidEmail(email)).isFalse();
}
private static Stream<String> provideInvalidEmails() {
return Stream.of(
"invalid",
"@example.com",
"user@",
"user @example.com"
);
}
}Test Fixtures
class UserTestFixture {
public static User createTestUser() {
User user = new User();
user.setId(1L);
user.setEmail("test@example.com");
user.setName("Test User");
return user;
}
public static CreateUserRequest createTestRequest() {
CreateUserRequest request = new CreateUserRequest();
request.setEmail("new@example.com");
request.setName("New User");
return request;
}
}
class UserServiceTest {
@Test
void shouldCreateUser() {
CreateUserRequest request = UserTestFixture.createTestRequest();
User result = userService.createUser(request);
assertThat(result.getEmail()).isEqualTo(request.getEmail());
}
}Spring Boot Testing Workflow Patterns
Complete Database Integration Test Pattern
Scenario: Test a JPA repository with a real PostgreSQL database using Testcontainers.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
public class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveAndRetrieveUserFromDatabase() {
// Arrange
User user = new User();
user.setEmail("test@example.com");
user.setName("Test User");
// Act
User saved = userRepository.save(user);
userRepository.flush();
Optional<User> retrieved = userRepository.findByEmail("test@example.com");
// Assert
assertThat(retrieved).isPresent();
assertThat(retrieved.get().getName()).isEqualTo("Test User");
}
@Test
void shouldThrowExceptionForDuplicateEmail() {
// Arrange
User user1 = new User();
user1.setEmail("duplicate@example.com");
user1.setName("User 1");
User user2 = new User();
user2.setEmail("duplicate@example.com");
user2.setName("User 2");
userRepository.save(user1);
// Act & Assert
assertThatThrownBy(() -> {
userRepository.save(user2);
userRepository.flush();
}).isInstanceOf(DataIntegrityViolationException.class);
}
}Complete REST API Integration Test Pattern
Scenario: Test REST controllers with full Spring context using MockMvc.
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository.deleteAll();
}
@Test
void shouldCreateUserAndReturn201() throws Exception {
User user = new User();
user.setEmail("newuser@example.com");
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("newuser@example.com"))
.andExpect(jsonPath("$.name").value("New User"));
}
@Test
void shouldReturnUserById() throws Exception {
// Arrange
User user = new User();
user.setEmail("existing@example.com");
user.setName("Existing User");
User saved = userRepository.save(user);
// Act & Assert
mockMvc.perform(get("/api/users/" + saved.getId())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("existing@example.com"))
.andExpect(jsonPath("$.name").value("Existing User"));
}
@Test
void shouldReturnNotFoundForMissingUser() throws Exception {
mockMvc.perform(get("/api/users/99999")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
@Test
void shouldUpdateUserAndReturn200() throws Exception {
// Arrange
User user = new User();
user.setEmail("update@example.com");
user.setName("Original Name");
User saved = userRepository.save(user);
User updateData = new User();
updateData.setName("Updated Name");
// Act & Assert
mockMvc.perform(put("/api/users/" + saved.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updateData)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Updated Name"));
}
@Test
void shouldDeleteUserAndReturn204() throws Exception {
// Arrange
User user = new User();
user.setEmail("delete@example.com");
user.setName("To Delete");
User saved = userRepository.save(user);
// Act & Assert
mockMvc.perform(delete("/api/users/" + saved.getId()))
.andExpect(status().isNoContent());
assertThat(userRepository.findById(saved.getId())).isEmpty();
}
}Service Layer Integration Test Pattern
Scenario: Test business logic with mocked repository.
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("test@example.com");
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
// Act
Optional<User> result = userService.findById(userId);
// Assert
assertThat(result).isPresent();
assertThat(result.get().getEmail()).isEqualTo("test@example.com");
verify(userRepository, times(1)).findById(userId);
}
@Test
void shouldReturnEmptyWhenUserNotFound() {
// Arrange
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
// Act
Optional<User> result = userService.findById(userId);
// Assert
assertThat(result).isEmpty();
verify(userRepository, times(1)).findById(userId);
}
@Test
void shouldThrowExceptionWhenSavingInvalidUser() {
// Arrange
User invalidUser = new User();
invalidUser.setEmail("invalid-email");
when(userRepository.save(invalidUser))
.thenThrow(new DataIntegrityViolationException("Invalid email"));
// Act & Assert
assertThatThrownBy(() -> userService.save(invalidUser))
.isInstanceOf(DataIntegrityViolationException.class);
}
}Reactive WebFlux Integration Test Pattern
Scenario: Test WebFlux controllers with WebTestClient.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class ReactiveUserControllerIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository.deleteAll();
}
@Test
void shouldReturnUserAsJsonReactive() {
// Arrange
User user = new User();
user.setEmail("reactive@example.com");
user.setName("Reactive User");
User saved = userRepository.save(user);
// Act & Assert
webTestClient.get()
.uri("/api/users/" + saved.getId())
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.email").isEqualTo("reactive@example.com")
.jsonPath("$.name").isEqualTo("Reactive User");
}
@Test
void shouldReturnArrayOfUsers() {
// Arrange
User user1 = new User();
user1.setEmail("user1@example.com");
user1.setName("User 1");
User user2 = new User();
user2.setEmail("user2@example.com");
user2.setName("User 2");
userRepository.saveAll(List.of(user1, user2));
// Act & Assert
webTestClient.get()
.uri("/api/users")
.exchange()
.expectStatus().isOk()
.expectBodyList(User.class)
.hasSize(2);
}
}Testcontainers Configuration Patterns
@ServiceConnection Pattern (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");
// Do not call start(); Spring Boot will manage lifecycle for @ServiceConnection beans
}
}@DynamicPropertySource Pattern (Legacy)
public class SharedContainers {
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();
}
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
registry.add("spring.datasource.username", POSTGRES::getUsername);
registry.add("spring.datasource.password", POSTGRES::getPassword);
}
}Slice Tests with Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
public class MyRepositoryIntegrationTest {
// repository tests
}Related skills
How it compares
Use spring-boot-test-patterns for opinionated Spring Boot JUnit and Testcontainers recipes; use generic TDD skills when the stack is not Java or Spring.
FAQ
What does spring-boot-test-patterns do?
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 te
When should I use spring-boot-test-patterns?
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 te
Is spring-boot-test-patterns safe to install?
Review the Security Audits panel on this page before installing in production.