🎯 Рекомендуемые коллекции
Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать
JUnit 5 Тестовый Фреймворк
Всесторонние примеры тестов JUnit 5 включая модульные тесты, интеграционные тесты, параметризованные тесты, мокирование с Mockito и продвинутые паттерны тестирования для Java приложений
💻 Базовая Настройка JUnit 5 java
🟢 simple
⭐
Полная настройка проекта JUnit 5 с конфигурацией Maven/Gradle, базовой структурой тестов и общими аннотациями
⏱️ 20 min
🏷️ junit5, setup, maven, gradle
Prerequisites:
Java basics, Maven/Gradle, Testing concepts
// 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.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 с Mockito и Testcontainers java
🔴 complex
⭐⭐⭐⭐
Продвинутые паттерны тестирования с использованием Mockito для мокирования и Testcontainers для интеграционных тестов
⏱️ 45 min
🏷️ junit5, mockito, testcontainers, integration
Prerequisites:
Java advanced, Spring Boot, Database concepts, Testing patterns
// 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);
}
}