Сегодня мы изучим основы Unit-тестирования с использованием JUnit 5.
Давайте разберемся, как использовать эту силу для создания качественного и безопасного кода.
Что такое JUnit 5 и зачем он нужен?
JUnit 5 — это современная версия популярного фреймворка для тестирования на Java. Его цель — помочь разработчикам писать качественные Unit-тесты. Преимущество JUnit 5 состоит в его модульности и гибкости. Вот основные компоненты JUnit 5:
- JUnit Platform: отвечает за запуск тестов.
- JUnit Jupiter: предоставляет новые функции и аннотации для тестирования.
- JUnit Vintage: позволяет запускать старые тесты, написанные на JUnit 3 и 4 (археологи, это для вас).
Почему JUnit 5? Потому что:
- Он поддерживает Java 8 и выше, включая лямбды и методы по умолчанию.
- Больше возможностей для настройки жизненного цикла тестов.
- Удобнейшая аннотация
@DisplayName, чтобы ваши тесты выглядели красиво и понятно.
Основные аннотации JUnit 5
Аннотации — это как указатели для JUnit, которые сообщают ему, что и как тестировать.
@Test
Самая главная аннотация. Если вы хотите, чтобы код выполнился как тест, просто пометьте метод этой аннотацией.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class MathTest {
@Test
void additionTest() {
int result = 2 + 3;
assertEquals(5, result, "2 + 3 должно быть равно 5");
}
}
@BeforeEach и @AfterEach
Используются для выполнения действий до и после каждого теста. Например, настройка тестовой среды или очистка ресурсов.
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
class LifecycleTest {
@BeforeEach
void setUp() {
System.out.println("Подготовка перед тестом");
}
@AfterEach
void tearDown() {
System.out.println("Очистка после теста");
}
@Test
void simpleTest() {
System.out.println("Выполнение теста");
}
}
Выводы будут такие:
Подготовка перед тестом
Выполнение теста
Очистка после теста
@BeforeAll и @AfterAll
Используются для выполнения действий до или после всех тестов в классе. Эти методы должны быть статическими.
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
class GlobalSetupTest {
@BeforeAll
static void init() {
System.out.println("Подготовка перед ВСЕМИ тестами");
}
@AfterAll
static void cleanup() {
System.out.println("Очистка после ВСЕХ тестов");
}
@Test
void testOne() {
System.out.println("Первый тест");
}
@Test
void testTwo() {
System.out.println("Второй тест");
}
}
Вывод:
Подготовка перед ВСЕМИ тестами
Первый тест
Второй тест
Очистка после ВСЕХ тестов
@DisplayName
Добавьте растение в цифровую пустыню ваших тестов! Делайте их красивее с @DisplayName.
@Test
@DisplayName("Проверка сложения: 2 + 2 = 4")
void additionTest() {
assertEquals(4, 2 + 2);
}
Основные Assertions
Assertions — это как контрольные точки. Они проверяют, правильно ли работает ваш код.
| Assertion | Описание | Пример |
|---|---|---|
assertEquals(expected, actual) |
Проверяет, равны ли ожидаемое и фактическое | assertEquals(4, 2 + 2) |
assertNotEquals(unexpected, actual) |
Проверяет, НЕ равны ли значения | assertNotEquals(5, 2 + 2) |
assertTrue(condition) |
Утверждает, что условие истина | assertTrue(2 + 2 == 4) |
assertFalse(condition) |
Утверждает, что условие ложно | assertFalse(2 + 2 == 5) |
assertNull(object) |
Проверяет, что объект равен null | assertNull(nullVariable) |
assertNotNull(object) |
Проверяет, что объект НЕ равен null | assertNotNull(nonNullVariable) |
assertThrows(exception, lambda) |
Проверяет, что код вызывает указанное исключение | assertThrows(NumberFormatException.class, () -> Integer.parseInt("NaN")) |
Пример:
@Test
void testAssertions() {
assertEquals(4, 2 + 2, "Сложение двух чисел должно быть корректным");
assertTrue(3 > 2, "3 больше 2 - это правда");
assertThrows(ArithmeticException.class, () -> {
int result = 10 / 0;
}, "Ожидалось ArithmeticException при делении на 0");
}
Жизненный цикл тестов в JUnit 5
Тесты в JUnit проходят через определённый жизненный цикл:
- Создаётся объект тестового класса.
- Вызывается метод с аннотацией @BeforeEach.
- Выполняется тест.
- Вызывается метод с аннотацией @AfterEach.
- Повторяется для каждого теста.
Методы с аннотациями @BeforeAll и @AfterAll выполняются один раз на весь класс.
Практика: Написание Unit-тестов для простых методов
Допустим, у нас есть класс Calculator:
class Calculator {
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Деление на ноль не допускается");
}
return a / b;
}
}
И тесты к нему:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
Calculator calculator = new Calculator();
@Test
void testAddition() {
assertEquals(5, calculator.add(2, 3), "2 + 3 должно быть равно 5");
}
@Test
void testSubtraction() {
assertEquals(1, calculator.subtract(3, 2), "3 - 2 должно быть равно 1");
}
@Test
void testMultiplication() {
assertEquals(6, calculator.multiply(2, 3), "2 * 3 должно быть равно 6");
}
@Test
void testDivision() {
assertEquals(2, calculator.divide(6, 3), "6 / 3 должно быть равно 2");
}
@Test
void testDivisionByZero() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> calculator.divide(5, 0));
assertEquals("Деление на ноль не допускается", exception.getMessage());
}
}
Эти тесты проверяют основные арифметические операции и обрабатывают исключение при делении на ноль.
Что оставляет чаще всего людей в замешательстве?
- Почему тесты не запускаются? Возможно, ваш метод в тестовом классе не помечен
@Test. - Почему "падает" тест? Прочтите сообщение ошибки внимательно. Скорее всего, ожидания
assertEqualsне совпадают с фактическим результатом. - Как писать тесты к приватным методам? Часто приватные методы тестируются косвенно через публичные.
JUnit 5 — мощный инструмент, и чем больше вы с ним практикуетесь, тем проще становится писать качественные тесты. Вперёд — ломайте свои баги в лучших традициях супергероев!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ