Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какой используешь JUnit
В современной Java разработке основной выбор — это JUnit 5 (также известный как Jupiter). Это новое поколение тестирующего фреймворка, которое пришло на смену JUnit 4. Расскажу почему и как его использовать.
История версий
JUnit 3 (2002) — древность, аннотаций не было
JUnit 4 (2006) — появились аннотации (@Test, @Before, @After)
JUnit 5 (2017) — полная переработка, модульная архитектура
JUnit 5 vs JUnit 4
Главные отличия:
// JUnit 4
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
public class UserServiceTest {
private UserService userService;
@Before
public void setUp() {
userService = new UserService();
}
@After
public void tearDown() {
// очистка
}
@Test
public void testFindUser() {
// тест
}
}
// JUnit 5 (современный подход)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
public class UserServiceTest {
private UserService userService;
@BeforeEach
void setUp() {
userService = new UserService();
}
@AfterEach
void tearDown() {
// очистка
}
@Test
@DisplayName("Should find user by ID successfully") // понятное имя!
void shouldFindUserById() {
// тест
}
}
Зависимости в Maven
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- Для Spring тестов -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Полный пример теста с JUnit 5
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("User Service Tests")
class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository = new InMemoryUserRepository();
userService = new UserService(userRepository);
}
@Nested
@DisplayName("Creating new users")
class CreateUserTests {
@Test
@DisplayName("Should create user with valid data")
void shouldCreateUserWithValidData() {
// Arrange (подготовка)
String email = "john@example.com";
String name = "John Doe";
// Act (действие)
User user = userService.createUser(name, email);
// Assert (проверка)
assertNotNull(user);
assertEquals(name, user.getName());
assertEquals(email, user.getEmail());
assertTrue(user.isActive());
}
@Test
@DisplayName("Should throw exception for invalid email")
void shouldThrowExceptionForInvalidEmail() {
// Verify (проверка исключений)
Exception exception = assertThrows(
IllegalArgumentException.class,
() -> userService.createUser("John", "invalid-email")
);
assertTrue(exception.getMessage().contains("Invalid email"));
}
@ParameterizedTest
@ValueSource(strings = {"test@example.com", "user@domain.co.uk", "name+tag@test.org"})
@DisplayName("Should accept various valid email formats")
void shouldAcceptVariousEmailFormats(String email) {
// Act
User user = userService.createUser("John", email);
// Assert
assertEquals(email, user.getEmail());
}
}
@Nested
@DisplayName("Finding users")
class FindUserTests {
@Test
void shouldFindUserById() {
// Arrange
User createdUser = userService.createUser("John", "john@example.com");
// Act
User foundUser = userService.findById(createdUser.getId());
// Assert
assertEquals(createdUser, foundUser);
}
@Test
void shouldReturnEmptyOptionalForNonexistentUser() {
// Act
Optional<User> user = userService.findById(999L);
// Assert
assertTrue(user.isEmpty());
}
}
@AfterEach
void tearDown() {
userRepository.clear();
}
}
Аннотации JUnit 5
@Test // Помечает метод как тест
@DisplayName("...") // Понятное имя для отчёта
@BeforeEach // Выполняется перед каждым @Test
@AfterEach // Выполняется после каждого @Test
@BeforeAll // Выполняется один раз перед всеми тестами (статический метод!)
@AfterAll // Выполняется один раз после всех тестов
@Nested // Группировка тестов (внутренние классы)
@ParameterizedTest // Параметризованный тест (разные данные)
@Disabled // Отключить тест
@Tag("integration") // Тег для фильтрации тестов
Assertions в JUnit 5
// Основные проверки
assertTrue(condition);
assertFalse(condition);
assertNull(value);
assertNotNull(value);
// Проверка равенства
assertEquals(expected, actual);
assertNotEquals(unexpected, actual);
// Проверка исключений
assertThrows(Exception.class, () -> code());
assertDoesNotThrow(() -> code());
// Проверка производительности
assertTimeout(Duration.ofMillis(100), () -> slowCode());
// Множественные проверки
assertAll(
() -> assertEquals("John", user.getName()),
() -> assertEquals("john@example.com", user.getEmail()),
() -> assertTrue(user.isActive())
);
Интеграция с Mockito (для mock'ирования)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldCallRepositoryWhenFindingUser() {
// Arrange
User expectedUser = new User("John", "john@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(expectedUser));
// Act
Optional<User> result = userService.findById(1L);
// Assert
assertTrue(result.isPresent());
assertEquals(expectedUser, result.get());
verify(userRepository, times(1)).findById(1L);
}
}
Spring интеграция
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
@ActiveProfiles("test")
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
@DisplayName("POST /users should create user")
void shouldCreateUser() throws Exception {
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\": \"John\", \"email\": \"john@example.com\"}")
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.name").value("John"));
}
}
Лучшие практики
- AAA pattern — Arrange, Act, Assert
@Test
void test() {
// Arrange: подготовка данных
User user = new User("John", "john@example.com");
// Act: выполнение действия
User savedUser = userService.save(user);
// Assert: проверка результата
assertNotNull(savedUser.getId());
}
- Читаемые имена тестов
// ❌ Плохо
void testUser() { }
// ✅ Хорошо
@DisplayName("Should save user with valid email")
void shouldSaveUserWithValidEmail() { }
- One assertion per test (когда возможно)
// ❌ Плохо — несколько проверок
@Test
void testUserCreation() {
User user = userService.create("John", "john@example.com");
assertEquals("John", user.getName());
assertEquals("john@example.com", user.getEmail());
assertTrue(user.isActive());
}
// ✅ Хорошо — каждый тест проверяет одно
@Test
void shouldHaveCorrectName() {
User user = userService.create("John", "john@example.com");
assertEquals("John", user.getName());
}
- Тестовое покрытие >= 80-90%
# Запуск тестов с покрытием
mvn clean test jacoco:report
# Отчёт в target/site/jacoco/
Выводы
✅ Используй JUnit 5 — это стандарт на 2024-2026 ✅ Комбинируй с Mockito — для изоляции тестов ✅ Пишу параметризованные тесты — избегай дублирования ✅ Юзай @DisplayName — тесты должны быть понятны бизнесу ✅ Тестовое покрытие > 80% — качество кода
Если твой проект всё ещё на JUnit 4 — пора мигрировать. JUnit 5 — это будущее Java тестирования.