Framework de Test JUnit 5
Exemples complets de tests JUnit 5 incluant tests unitaires, tests d'intégration, tests paramétrés, mocking avec Mockito et patterns de test avancés pour applications Java
Key Facts
- Category
- Testing
- Items
- 2
- Format Families
- sample
Sample Overview
Exemples complets de tests JUnit 5 incluant tests unitaires, tests d'intégration, tests paramétrés, mocking avec Mockito et patterns de test avancés pour applications Java This sample set belongs to Testing and can be used to test related workflows inside Elysia Tools.
💻 Configuration de Base JUnit 5 java
Configuration complète du projet JUnit 5 avec configuration Maven/Gradle, structure de test de base et annotations communes
// JUnit 5 Configuration Examples
// 1. Maven Configuration - pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-testing-example</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- JUnit 5 version -->
<junit.version>5.10.1</junit.version>
<!-- Mockito version -->
<mockito.version>5.7.0</mockito.version>
<!-- AssertJ version -->
<assertj.version>3.24.2</assertj.version>
</properties>
<dependencies>
<!-- JUnit 5 Jupiter -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 Parameterized Tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 Suite Support -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.10.1</version>
<scope>test</scope>
</dependency>
<!-- Mockito for mocking -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- Mockito with JUnit 5 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<!-- AssertJ for fluent assertions -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers for integration testing -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<!-- Maven Surefire Plugin for running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<!-- Include only JUnit 5 tests -->
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<!-- Test configuration -->
<systemPropertyVariables>
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
</systemPropertyVariables>
<!-- Parallel test execution -->
<parallel>methods</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>
<!-- JaCoCo for code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
// 2. Gradle Configuration - build.gradle.kts
plugins {
java
jacoco
application
}
group = "com.example"
version = "1.0.0"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
// JUnit 5
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
testImplementation("org.junit.platform:junit-platform-suite:1.10.1")
// Mockito
testImplementation("org.mockito:mockito-core:5.7.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.7.0")
// AssertJ
testImplementation("org.assertj:assertj-core:3.24.2")
// Testcontainers
testImplementation("org.testcontainers:junit-jupiter:1.19.3")
}
tasks.test {
useJUnitPlatform()
// Configure test execution
systemProperties("java.util.logging.config.file" to "src/test/resources/logging.properties")
// Parallel execution
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
// Test logging
testLogging {
events("passed", "skipped", "failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}
// JaCoCo configuration
jacoco {
toolVersion = "0.8.11"
}
tasks.jacocoTestReport {
dependsOn(tasks.test)
reports {
xml.required.set(true)
html.required.set(true)
}
}
// 3. JUnit 5 Configuration - src/test/resources/junit-platform.properties
# JUnit Platform Configuration
# Enable parallel execution
junit.jupiter.execution.parallel.enabled = true
# Parallel execution mode: same_thread, concurrent
junit.jupiter.execution.parallel.mode.default = concurrent
# Class-level parallel mode: same_thread, concurrent
junit.jupiter.execution.parallel.mode.classes.default = concurrent
# Test instance lifecycle: per_class, per_method
junit.jupiter.testinstance.lifecycle.default = per_class
# Display test execution
junit.jupiter.extensions.autodetection.enabled = true
# Condition execution
junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
# 4. src/test/resources/logback-test.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/test.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
// 5. src/main/java/com/example/calculator/Calculator.java
package com.example.calculator;
public class Calculator {
public double add(double a, double b) {
return a + b;
}
public double subtract(double a, double b) {
return a - b;
}
public double multiply(double a, double b) {
return a * b;
}
public double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Cannot divide by zero");
}
return a / b;
}
public double power(double base, double exponent) {
return Math.pow(base, exponent);
}
public double sqrt(double number) {
if (number < 0) {
throw new IllegalArgumentException("Cannot calculate square root of negative number");
}
return Math.sqrt(number);
}
}
// 6. src/test/java/com/example/calculator/CalculatorTest.java
package com.example.calculator;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.*;
class CalculatorTest {
private Calculator calculator;
// @BeforeEach - Run before each test
@BeforeEach
void setUp() {
calculator = new Calculator();
System.out.println("Setting up Calculator instance");
}
// @AfterEach - Run after each test
@AfterEach
void tearDown() {
calculator = null;
System.out.println("Cleaning up Calculator instance");
}
// @BeforeAll - Run once before all tests
@BeforeAll
static void initAll() {
System.out.println("Initializing all tests");
}
// @AfterAll - Run once after all tests
@AfterAll
static void tearDownAll() {
System.out.println("Cleaning up all tests");
}
// Basic test with JUnit 5 assertions
@Test
@DisplayName("Should add two numbers correctly")
@Tag("basic")
void shouldAddTwoNumbers() {
// Given
double a = 5.0;
double b = 3.0;
// When
double result = calculator.add(a, b);
// Then
assertEquals(8.0, result, "5 + 3 should equal 8");
assertThat(result).isEqualTo(8.0);
}
@Test
@DisplayName("Should subtract two numbers correctly")
@Tag("basic")
void shouldSubtractTwoNumbers() {
// Given
double a = 10.0;
double b = 4.0;
// When
double result = calculator.subtract(a, b);
// Then
assertEquals(6.0, result, "10 - 4 should equal 6");
assertThat(result).isPositive().isGreaterThan(5.0);
}
@Test
@DisplayName("Should multiply two numbers correctly")
@Tag("basic")
void shouldMultiplyTwoNumbers() {
// Given
double a = 4.0;
double b = 3.0;
// When
double result = calculator.multiply(a, b);
// Then
assertEquals(12.0, result, "4 * 3 should equal 12");
assertThat(result).isEqualTo(12.0);
}
@Test
@DisplayName("Should divide two numbers correctly")
@Tag("basic")
void shouldDivideTwoNumbers() {
// Given
double a = 15.0;
double b = 3.0;
// When
double result = calculator.divide(a, b);
// Then
assertEquals(5.0, result, 0.001, "15 / 3 should equal 5");
assertThat(result).isBetween(4.9, 5.1);
}
@Test
@DisplayName("Should throw exception when dividing by zero")
@Tag("exception")
void shouldThrowExceptionWhenDividingByZero() {
// Given
double a = 10.0;
double b = 0.0;
// When & Then
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> calculator.divide(a, b),
"Should throw IllegalArgumentException"
);
assertEquals("Cannot divide by zero", exception.getMessage());
// Alternative with AssertJ
assertThatThrownBy(() -> calculator.divide(a, b))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Cannot divide by zero");
}
// Parameterized test with @ValueSource
@ParameterizedTest
@ValueSource(doubles = {0.0, 1.0, 4.0, 9.0, 16.0, 25.0})
@DisplayName("Should calculate square root correctly")
@Tag("parameterized")
void shouldCalculateSquareRootCorrectly(double number) {
// When
double result = calculator.sqrt(number);
// Then
assertThat(result).isGreaterThanOrEqualTo(0.0);
assertThat(result * result).isCloseTo(number, within(0.001));
}
@ParameterizedTest
@ValueSource(doubles = {-1.0, -4.0, -9.0})
@DisplayName("Should throw exception for negative square root")
@Tag("parameterized")
void shouldThrowExceptionForNegativeSquareRoot(double number) {
assertThatThrownBy(() -> calculator.sqrt(number))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Cannot calculate square root of negative number");
}
// Parameterized test with @CsvSource
@ParameterizedTest(name = "Test {index}: {0} ^ {1} = {2}")
@CsvSource({
"2.0, 3.0, 8.0",
"3.0, 2.0, 9.0",
"5.0, 0.0, 1.0",
"10.0, 1.0, 10.0"
})
@DisplayName("Should calculate power correctly")
@Tag("parameterized")
void shouldCalculatePowerCorrectly(double base, double exponent, double expected) {
// When
double result = calculator.power(base, exponent);
// Then
assertThat(result).isCloseTo(expected, within(0.001));
}
// Parameterized test with @MethodSource
@ParameterizedTest
@MethodSource("provideMultiplicationData")
@DisplayName("Should multiply with various inputs")
@Tag("parameterized")
void shouldMultiplyWithVariousInputs(double a, double b, double expected) {
// When
double result = calculator.multiply(a, b);
// Then
assertThat(result).isCloseTo(expected, within(0.001));
}
static Stream<Arguments> provideMultiplicationData() {
return Stream.of(
Arguments.of(2.5, 4.0, 10.0),
Arguments.of(-3.0, 2.0, -6.0),
Arguments.of(0.0, 5.0, 0.0),
Arguments.of(1.5, 1.5, 2.25)
);
}
// Nested test class
@Nested
@DisplayName("Edge Case Tests")
@Tag("edge-case")
class EdgeCaseTests {
@Test
@DisplayName("Should handle very large numbers")
void shouldHandleVeryLargeNumbers() {
// Given
double largeNumber = Double.MAX_VALUE / 2;
// When
double result = calculator.add(largeNumber, 1.0);
// Then
assertThat(result).isGreaterThan(largeNumber);
assertThat(Double.isInfinite(result)).isFalse();
}
@Test
@DisplayName("Should handle floating point precision")
void shouldHandleFloatingPointPrecision() {
// Given
double a = 0.1;
double b = 0.2;
// When
double result = calculator.add(a, b);
// Then
assertThat(result).isCloseTo(0.3, within(0.001));
}
}
// Disabled test
@Test
@Disabled("This test is disabled for demonstration")
@DisplayName("Disabled test example")
void disabledTestExample() {
fail("This test should not run");
}
// Repeated test
@RepeatedTest(3)
@DisplayName("Repeated test example")
void repeatedTestExample(RepetitionInfo repetitionInfo) {
System.out.println("Running repetition " + repetitionInfo.getCurrentRepetition());
assertTrue(calculator.add(1, 1) == 2);
}
// Test timeout
@Test
@Timeout(1) // 1 second timeout
@DisplayName("Should complete within timeout")
void shouldCompleteWithinTimeout() throws InterruptedException {
// This test should complete within 1 second
Thread.sleep(100);
assertTrue(calculator.add(1, 1) == 2);
}
// Custom assumptions
@Test
@DisplayName("Should run only on certain conditions")
void shouldRunOnlyOnCertainConditions() {
// Skip test if running on Windows
Assumptions.assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"),
"Test skipped on Windows");
// Continue with test
assertThat(calculator.add(2, 3)).isEqualTo(5);
}
}
💻 JUnit 5 avec Mockito et Testcontainers java
Patterns de test avancés utilisant Mockito pour mocking et Testcontainers pour tests d'intégration
// JUnit 5 Advanced Testing with Mockito and Testcontainers
// 1. src/main/java/com/example/service/UserService.java
package com.example.service;
import com.example.model.User;
import com.example.repository.UserRepository;
import com.example.exception.UserNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User createUser(User user) {
user.setCreatedAt(LocalDateTime.now());
user.setActive(true);
User savedUser = userRepository.save(user);
// Send welcome email
emailService.sendWelcomeEmail(savedUser.getEmail(), savedUser.getUsername());
return savedUser;
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
public List<User> getAllActiveUsers() {
return userRepository.findByActiveTrue();
}
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
user.setUsername(userDetails.getUsername());
user.setEmail(userDetails.getEmail());
user.setUpdatedAt(LocalDateTime.now());
return userRepository.save(user);
}
public void deleteUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
userRepository.delete(user);
}
public User deactivateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
user.setActive(false);
user.setDeactivatedAt(LocalDateTime.now());
return userRepository.save(user);
}
}
// 2. src/main/java/com/example/service/EmailService.java
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
public void sendWelcomeEmail(String email, String username) {
// Implementation would send actual email
System.out.println("Sending welcome email to: " + email);
}
public void sendPasswordResetEmail(String email, String resetToken) {
// Implementation would send password reset email
System.out.println("Sending password reset email to: " + email);
}
}
// 3. src/main/java/com/example/model/User.java
package com.example.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private Boolean active = true;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Column(name = "deactivated_at")
private LocalDateTime deactivatedAt;
// Constructors
public User() {}
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
public LocalDateTime getDeactivatedAt() { return deactivatedAt; }
public void setDeactivatedAt(LocalDateTime deactivatedAt) { this.deactivatedAt = deactivatedAt; }
}
// 4. src/test/java/com/example/service/UserServiceMockitoTest.java
package com.example.service;
import com.example.model.User;
import com.example.repository.UserRepository;
import com.example.exception.UserNotFoundException;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
@DisplayName("UserService Tests with Mockito")
class UserServiceMockitoTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Captor
private ArgumentCaptor<User> userCaptor;
@Test
@DisplayName("Should create user successfully")
void shouldCreateUserSuccessfully() {
// Given
User userToCreate = new User("testuser", "[email protected]", "password123");
User savedUser = new User(1L, "testuser", "[email protected]", "password123");
savedUser.setCreatedAt(LocalDateTime.now());
savedUser.setActive(true);
when(userRepository.save(any(User.class))).thenReturn(savedUser);
// When
User result = userService.createUser(userToCreate);
// Then
assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo(1L);
assertThat(result.getUsername()).isEqualTo("testuser");
assertThat(result.getEmail()).isEqualTo("[email protected]");
assertThat(result.getActive()).isTrue();
assertThat(result.getCreatedAt()).isNotNull();
// Verify interactions
verify(userRepository, times(1)).save(userCaptor.capture());
verify(emailService, times(1)).sendWelcomeEmail("[email protected]", "testuser");
// Verify captured user
User capturedUser = userCaptor.getValue();
assertThat(capturedUser.getUsername()).isEqualTo("testuser");
assertThat(capturedUser.getActive()).isTrue();
}
@Test
@DisplayName("Should get user by ID when user exists")
void shouldGetUserByIdWhenUserExists() {
// Given
User user = new User(1L, "testuser", "[email protected]", "password123");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
// When
Optional<User> result = userService.getUserById(1L);
// Then
assertThat(result).isPresent();
assertThat(result.get().getId()).isEqualTo(1L);
assertThat(result.get().getUsername()).isEqualTo("testuser");
verify(userRepository, times(1)).findById(1L);
}
@Test
@DisplayName("Should return empty when user does not exist")
void shouldReturnEmptyWhenUserDoesNotExist() {
// Given
when(userRepository.findById(999L)).thenReturn(Optional.empty());
// When
Optional<User> result = userService.getUserById(999L);
// Then
assertThat(result).isEmpty();
verify(userRepository, times(1)).findById(999L);
}
@Test
@DisplayName("Should get all active users")
void shouldGetAllActiveUsers() {
// Given
List<User> activeUsers = Arrays.asList(
new User(1L, "user1", "[email protected]", "pass1"),
new User(2L, "user2", "[email protected]", "pass2")
);
when(userRepository.findByActiveTrue()).thenReturn(activeUsers);
// When
List<User> result = userService.getAllActiveUsers();
// Then
assertThat(result).hasSize(2);
assertThat(result.get(0).getUsername()).isEqualTo("user1");
assertThat(result.get(1).getUsername()).isEqualTo("user2");
verify(userRepository, times(1)).findByActiveTrue();
}
@Test
@DisplayName("Should update user successfully")
void shouldUpdateUserSuccessfully() {
// Given
User existingUser = new User(1L, "olduser", "[email protected]", "password");
User userDetails = new User("newuser", "[email protected]", "newpassword");
when(userRepository.findById(1L)).thenReturn(Optional.of(existingUser));
when(userRepository.save(any(User.class))).thenAnswer(invocation -> invocation.getArgument(0));
// When
User result = userService.updateUser(1L, userDetails);
// Then
assertThat(result.getUsername()).isEqualTo("newuser");
assertThat(result.getEmail()).isEqualTo("[email protected]");
assertThat(result.getUpdatedAt()).isNotNull();
verify(userRepository, times(1)).findById(1L);
verify(userRepository, times(1)).save(any(User.class));
}
@Test
@DisplayName("Should throw exception when updating non-existent user")
void shouldThrowExceptionWhenUpdatingNonExistentUser() {
// Given
User userDetails = new User("newuser", "[email protected]", "newpassword");
when(userRepository.findById(999L)).thenReturn(Optional.empty());
// When & Then
UserNotFoundException exception = assertThrows(
UserNotFoundException.class,
() -> userService.updateUser(999L, userDetails)
);
assertThat(exception.getMessage()).isEqualTo("User not found with id: 999");
verify(userRepository, times(1)).findById(999L);
verify(userRepository, never()).save(any(User.class));
}
@Test
@DisplayName("Should delete user successfully")
void shouldDeleteUserSuccessfully() {
// Given
User user = new User(1L, "testuser", "[email protected]", "password");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
doNothing().when(userRepository).delete(user);
// When
userService.deleteUser(1L);
// Then
verify(userRepository, times(1)).findById(1L);
verify(userRepository, times(1)).delete(user);
}
@Test
@DisplayName("Should deactivate user successfully")
void shouldDeactivateUserSuccessfully() {
// Given
User user = new User(1L, "testuser", "[email protected]", "password");
user.setActive(true);
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
when(userRepository.save(any(User.class))).thenAnswer(invocation -> invocation.getArgument(0));
// When
User result = userService.deactivateUser(1L);
// Then
assertThat(result.getActive()).isFalse();
assertThat(result.getDeactivatedAt()).isNotNull();
verify(userRepository, times(1)).findById(1L);
verify(userRepository, times(1)).save(user);
}
@Test
@DisplayName("Should throw exception when deactivating non-existent user")
void shouldThrowExceptionWhenDeactivatingNonExistentUser() {
// Given
when(userRepository.findById(999L)).thenReturn(Optional.empty());
// When & Then
UserNotFoundException exception = assertThrows(
UserNotFoundException.class,
() -> userService.deactivateUser(999L)
);
assertThat(exception.getMessage()).isEqualTo("User not found with id: 999");
verify(userRepository, times(1)).findById(999L);
verify(userRepository, never()).save(any(User.class));
}
// Test with multiple verifications
@Test
@DisplayName("Should create multiple users and verify all interactions")
void shouldCreateMultipleUsersAndVerifyAllInteractions() {
// Given
User user1 = new User("user1", "[email protected]", "password1");
User user2 = new User("user2", "[email protected]", "password2");
User savedUser1 = new User(1L, "user1", "[email protected]", "password1");
User savedUser2 = new User(2L, "user2", "[email protected]", "password2");
when(userRepository.save(any(User.class)))
.thenReturn(savedUser1)
.thenReturn(savedUser2);
// When
userService.createUser(user1);
userService.createUser(user2);
// Then
verify(userRepository, times(2)).save(any(User.class));
verify(emailService, times(1)).sendWelcomeEmail("[email protected]", "user1");
verify(emailService, times(1)).sendWelcomeEmail("[email protected]", "user2");
// Verify order of interactions
InOrder inOrder = inOrder(userRepository, emailService);
inOrder.verify(userRepository).save(any(User.class));
inOrder.verify(emailService).sendWelcomeEmail("[email protected]", "user1");
inOrder.verify(userRepository).save(any(User.class));
inOrder.verify(emailService).sendWelcomeEmail("[email protected]", "user2");
}
}
// 5. src/test/java/com/example/repository/UserRepositoryIntegrationTest.java
package com.example.repository;
import com.example.model.User;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
@DataJpaTest
@Testcontainers
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("UserRepository Integration Tests")
class UserRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
private User testUser;
@BeforeEach
void setUp() {
testUser = new User("testuser", "[email protected]", "password123");
testUser.setCreatedAt(LocalDateTime.now());
testUser.setActive(true);
}
@Test
@Order(1)
@DisplayName("Should save user successfully")
void shouldSaveUserSuccessfully() {
// When
User savedUser = userRepository.save(testUser);
// Then
assertThat(savedUser).isNotNull();
assertThat(savedUser.getId()).isNotNull();
assertThat(savedUser.getUsername()).isEqualTo("testuser");
assertThat(savedUser.getEmail()).isEqualTo("[email protected]");
assertThat(savedUser.getActive()).isTrue();
}
@Test
@Order(2)
@DisplayName("Should find user by ID")
void shouldFindUserById() {
// Given
User savedUser = userRepository.save(testUser);
// When
Optional<User> foundUser = userRepository.findById(savedUser.getId());
// Then
assertThat(foundUser).isPresent();
assertThat(foundUser.get().getId()).isEqualTo(savedUser.getId());
assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
}
@Test
@Order(3)
@DisplayName("Should find user by email")
void shouldFindUserByEmail() {
// Given
userRepository.save(testUser);
// When
Optional<User> foundUser = userRepository.findByEmail("[email protected]");
// Then
assertThat(foundUser).isPresent();
assertThat(foundUser.get().getEmail()).isEqualTo("[email protected]");
assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
}
@Test
@Order(4)
@DisplayName("Should find user by username")
void shouldFindUserByUsername() {
// Given
userRepository.save(testUser);
// When
Optional<User> foundUser = userRepository.findByUsername("testuser");
// Then
assertThat(foundUser).isPresent();
assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
assertThat(foundUser.get().getEmail()).isEqualTo("[email protected]");
}
@Test
@Order(5)
@DisplayName("Should find all active users")
void shouldFindAllActiveUsers() {
// Given
User activeUser = new User("activeuser", "[email protected]", "password");
activeUser.setActive(true);
activeUser.setCreatedAt(LocalDateTime.now());
User inactiveUser = new User("inactiveuser", "[email protected]", "password");
inactiveUser.setActive(false);
inactiveUser.setCreatedAt(LocalDateTime.now());
userRepository.save(activeUser);
userRepository.save(inactiveUser);
userRepository.save(testUser);
// When
List<User> activeUsers = userRepository.findByActiveTrue();
// Then
assertThat(activeUsers).hasSize(2);
assertThat(activeUsers.stream().allMatch(User::getActive)).isTrue();
assertThat(activeUsers).noneMatch(user -> "inactiveuser".equals(user.getUsername()));
}
@Test
@Order(6)
@DisplayName("Should check if email exists")
void shouldCheckIfEmailExists() {
// Given
userRepository.save(testUser);
// When & Then
assertThat(userRepository.existsByEmail("[email protected]")).isTrue();
assertThat(userRepository.existsByEmail("[email protected]")).isFalse();
}
@Test
@Order(7)
@DisplayName("Should check if username exists")
void shouldCheckIfUsernameExists() {
// Given
userRepository.save(testUser);
// When & Then
assertThat(userRepository.existsByUsername("testuser")).isTrue();
assertThat(userRepository.existsByUsername("nonexistent")).isFalse();
}
@Test
@Order(8)
@DisplayName("Should delete user by ID")
void shouldDeleteUserById() {
// Given
User savedUser = userRepository.save(testUser);
Long userId = savedUser.getId();
// When
userRepository.deleteById(userId);
// Then
Optional<User> deletedUser = userRepository.findById(userId);
assertThat(deletedUser).isEmpty();
}
@Test
@DisplayName("Should handle unique constraint violation")
void shouldHandleUniqueConstraintViolation() {
// Given
userRepository.save(testUser);
User duplicateUser = new User("differentuser", "[email protected]", "password");
// When & Then
assertThatThrownBy(() -> userRepository.saveAndFlush(duplicateUser))
.hasCauseInstanceOf(org.hibernate.exception.ConstraintViolationException.class);
}
}