JUnit Assertions — руководство по основным методам

Краткое введение
Класс JUnit Assertions содержит набор статических методов, с помощью которых проводятся модульные тесты. Assertions — одна из ключевых возможностей JUnit. В JUnit 5 этот класс включает более пятидесяти перегруженных методов для разных ситуаций: некоторые утверждения завершаются ошибкой, если условие истинно, другие — если условие ложно. Перегрузки позволяют передавать разные типы данных, сообщения об ошибках и ленивые генераторы сообщений (Supplier).
Пояснение терминов в одну строку:
- Assertions: набор статических методов для проверки состояния в тестах.
- Supplier: функциональный интерфейс, возвращающий строку сообщения по запросу.
- BooleanSupplier: функциональный интерфейс, возвращающий boolean.
Важно: модульные тесты проверяют единицы кода (функции, методы) изолированно от внешних зависимостей.
Содержание (навигация)
- assertEquals — сравнение ожидаемого и фактического значения
- assertNull — проверка на null
- assertTrue / assertFalse — логические проверки
- Примеры кода
- Лучшие практики и чек-лист по написанию тестов
- Ментальные модели и анти-паттерны
- Критерии приёмки и тест-кейсы
- Шпаргалка (cheat sheet)
- Глоссарий
assertEquals — сравнение ожидаемого и фактического значения
Метод assertEquals в JUnit 5 имеет много перегрузок (более десяти). Самая распространённая форма: указать ожидаемое значение (expected) и фактическое (actual). Дополнительные варианты принимают сообщение об ошибке или Supplier для ленивой генерации сообщения. Некоторые перегрузки принимают параметр delta для сравнения чисел с плавающей запятой.
Когда использовать:
- При детерминированном результате функции.
- При сравнении простых значений, объектов с корректно реализованным equals(), или массивов/коллекций — при использовании специализированных методов/утилит.
Пример класса с тестируемым методом (код Java):
package com.program;
public class AssertionsMethods {
public static int square(int num) {
return num * num;
}
}Пример JUnit 5 теста с корректными вызовами assertEquals:
package com.program;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class AssertionsMethodsTest {
@Test
void testSquare() {
assertEquals(25, AssertionsMethods.square(5));
assertEquals(36, AssertionsMethods.square(6), "Ваши значения квадрата не совпали.");
assertEquals(49, AssertionsMethods.square(7), () -> "Ваши значения квадрата не совпали.");
}
}Пояснения:
- Перегрузка с Supplier полезна, когда формирование сообщения дорогостоящее — сообщение создаётся только при неудаче.
- Для чисел с плавающей запятой используйте перегрузку с delta: assertEquals(expected, actual, delta).
Когда assertEquals может подвести:
- Когда equals() на сравниваемых объектах не реализован корректно.
- При сравнении коллекций — порядок элементов имеет значение.
Альтернатива: для сравнения содержимого коллекций/массивов используйте специализированные средства, например assertIterableEquals, assertArrayEquals, или библиотеки сравнения (Hamcrest, AssertJ) для более выразительных сообщений.
assertNull — проверка на null
Класс Assertions содержит ровно три перегрузки assertNull. Они проверяют, что объект равен null, и падают, если объект не null.
Пример:
@Test
public void testStringValue() {
String stringValue = null;
assertNull(stringValue);
assertNull(stringValue, "Ваше строковое значение не равно null");
assertNull(stringValue, () -> "Ваше строковое значение не равно null");
}Подсказки:
- assertNull полезен для проверки инициализации, результатов методов-фабрик или поведения при ошибках.
- Если ожидается пустая строка, применяйте assertEquals(“”, str) — null и пустая строка различаются.
Пример, когда assertNull не подходит:
- Объект может быть не null, но находиться в состоянии «неинициализирован», тогда лучше проверять конкретные поля/свойства.
assertTrue / assertFalse — логические утверждения
assertTrue и assertFalse позволяют проверять булевы условия. У каждого метода есть по шесть перегрузок, позволяющих передавать:
- простое boolean-условие и сообщение,
- BooleanSupplier (лямбда),
- Supplier
для ленивого сообщения.
Пример использования assertTrue (включая все варианты):
@Test
void testEvenNumbers() {
int num1 = 10;
int num2 = 16;
int num3 = 26;
assertTrue(num1 < num2);
assertTrue(num3 > num2, "Ваше условие не истинно.");
assertTrue(num1 < num3, () -> "Ваше условие не истинно.");
assertTrue(() -> num1 % 2 == 0);
assertTrue(() -> num2 % 2 == 0, "Ваше значение не является чётным числом.");
assertTrue(() -> num3 % 2 == 0, () -> "Ваше значение не является чётным числом.");
}Пример assertFalse:
@Test
void testNotEvenNumbers() {
int num1 = 11;
int num2 = 17;
int num3 = 27;
assertFalse(num2 < num1);
assertFalse(num2 > num3, "Ваше условие не ложно.");
assertFalse(num3 < num1, () -> "Ваше условие не ложно.");
assertFalse(() -> num1 % 2 == 0);
assertFalse(() -> num2 % 2 == 0, "Ваше значение — чётное число.");
assertFalse(() -> num3 % 2 == 0, () -> "Ваше значение — чётное число.");
}Хорошая практика: при проверке сложных условий предпочитайте BooleanSupplier (лямбды) — это делает тесты более читаемыми и предоставляет возможность ленивого вычисления.
Преимущества модульного тестирования
Модульное тестирование помогает обнаруживать ошибки на ранних этапах разработки. Это способствует:
- улучшению дизайна кода (модули с высокой тестируемостью обычно имеют низкую связанность),
- документированию ожидаемого поведения в виде тестов,
- упрощению поддержки и рефакторинга.
Важно: модульные тесты — не единственный вид тестирования. Интеграционные и системные тесты дополняют их и проверяют взаимодействие между компонентами.
Практическое руководство: мини-методология для написания модульных тестов
- Определите единицу тестирования (класс/метод).
- Выделите сценарии: положительные, отрицательные, граничные.
- Для каждого сценария напишите один тестовый метод — один сценарий = один тест.
- Убедитесь, что тесты детерминированы и не зависят от внешнего состояния.
- Используйте стабы/моки для изоляции внешних зависимостей.
- Пишите читаемые сообщения об ошибках (или Supplier для ленивых сообщений).
- Поддерживайте тесты при рефакторинге: тесты — код, который нужно рефакторить тоже.
Чек-лист по написанию тестов (роль-ориентированный)
Для разработчика:
- Тест покрывает один сценарий.
- Нет «магических чисел» — используйте понятные константы.
- Тест не зависит от порядка выполнения других тестов.
Для тим-лида / ревьюера:
- Сообщение об ошибке информативно.
- Используются корректные assertion-методы (assertArrayEquals для массивов).
- Нет дублирующей логики теста в коде приложения.
Для инженера автоматизации:
- Тесты стабильно выполняются в CI.
- Нет таймаутов/зависаний на сторонних ресурсах.
Критерии приёмки (пример)
- Все тесты выполняются локально и в CI без ожидания внешних сервисов.
- Тесты покрывают критические сценарии бизнес-логики.
- При изменении спецификации поведение теста обновлено или тест помечен как устаревший.
Шпаргалка (cheat sheet)
- assertEquals(expected, actual) — базовое сравнение.
- assertEquals(expected, actual, message) — с сообщением.
- assertEquals(expected, actual, ()->message) — ленивое сообщение.
- assertNull(obj) / assertNotNull(obj).
- assertTrue(cond) / assertFalse(cond).
- assertArrayEquals(expectedArray, actualArray) — для массивов.
- assertIterableEquals(expectedIterable, actualIterable) — для коллекций.
- assertThrows(Exception.class, () -> { … }) — проверка исключений.
Примеры тест-кейсов и критерии приёмки
Тест-кейс: метод square(int) возвращает квадрат числа.
- Вход: 5. Ожидаемый результат: 25.
- Критерий приёмки: assertEquals(25, square(5)).
Граничный тест: square(0) => 0.
- Критерий приёмки: assertEquals(0, square(0)).
Отрицательный сценарий (если применяется): если метод должен бросать исключение при входных данных вне диапазона — используйте assertThrows.
Ментальные модели и эвристики
- «Один тест — одна проверка» не всегда буквально: допустимо несколько assert-ов, если они проверяют разные аспекты одного сценария (при этом тест остаётся понятным).
- Предпочитайте малые, быстрые тесты: медленные тесты реже запускаются.
- Ленивая генерация сообщений (Supplier) экономит ресурсы при успешных тестах.
Частые ошибки и как их избежать (gallery of edge-cases)
- Смешивание JUnit 4 и JUnit 5 импортов — приводит к неожиданному поведению. Используйте единый набор зависимостей.
- Сравнение floating-point без delta — тест хрупок на разных JVM/платформах.
- Проверка побочных эффектов без изоляции — используйте моки/стабы.
- Тесты, зависящие от времени/локали — параметризуйте или используйте фиктивные часы.
Пример исправления для float:
- Неправильно: assertEquals(0.1 + 0.2, result);
- Правильно: assertEquals(expected, result, 1e-6);
Глоссарий (одна строка на термин)
- Assertion: утверждение в тесте, проверяющее состояние или результат.
- Supplier: функциональный интерфейс, который возвращает значение по запросу.
- BooleanSupplier: функциональный интерфейс, возвращающий boolean.
- DDT (Data-Driven Testing): подход к тестированию с входными данными из таблиц/файлов.
Итог
JUnit Assertions — это гибкий инструмент для проверки ожидаемого поведения единиц кода. Понимание перегрузок методов (сообщение vs Supplier, BooleanSupplier и delta для чисел) помогает писать надёжные и быстрые тесты. Следуйте простой методологии: один сценарий — один тест, изолируйте зависимости, используйте ленивые сообщения и специализированные assertion-методы для коллекций и массивов.
Важно: поддерживайте тесты в актуальном состоянии при рефакторинге — тесты становятся документацией для вашего кода.
Примечание: этот материал покрывает базовые утверждения; для сложных сравнений рассмотрите библиотеки AssertJ или Hamcrest для более выразительных и читаемых проверок.
Похожие материалы
Как изменить фото профиля YouTube
Организация списка для чтения — как управлять
Играть в GameCube на ПК с Dolphin
Импорт фото в Adobe Lightroom — руководство
Кольцевой свет в фотографии: как и когда использовать