← Назад к вопросам

Какой используешь JUnit?

1.0 Junior🔥 171 комментариев
#Основы Java#Тестирование

Комментарии (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"));
    }
}

Лучшие практики

  1. 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());
}
  1. Читаемые имена тестов
// ❌ Плохо
void testUser() { }

// ✅ Хорошо
@DisplayName("Should save user with valid email")
void shouldSaveUserWithValidEmail() { }
  1. 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());
}
  1. Тестовое покрытие >= 80-90%
# Запуск тестов с покрытием
mvn clean test jacoco:report

# Отчёт в target/site/jacoco/

Выводы

Используй JUnit 5 — это стандарт на 2024-2026 ✅ Комбинируй с Mockito — для изоляции тестов ✅ Пишу параметризованные тесты — избегай дублирования ✅ Юзай @DisplayName — тесты должны быть понятны бизнесу ✅ Тестовое покрытие > 80% — качество кода

Если твой проект всё ещё на JUnit 4 — пора мигрировать. JUnit 5 — это будущее Java тестирования.