W3docs

Утверждения Java JUnit

Проверяйте поведение в JUnit 5 с assertEquals, assertTrue, assertThrows, assertAll и другими методами.

Утверждение — это момент, когда тест перестаёт описывать и начинает проверять: оно задаёт, что должен вернуть код, и проваливает тест, если реальность не совпадает. JUnit 5 (API Jupiter) собирает все утверждения как static-методы одного класса — org.junit.jupiter.api.Assertions. Освоив этот небольшой набор методов, вы сможете выразить почти любое ожидание: равенство, истинность, нулевость, идентичность, выброшенные исключения, таймауты и групповые проверки. В этой главе рассматриваются те методы, к которым вы будете обращаться ежедневно.

Если вы новичок в этом фреймворке, сначала прочитайте введение в JUnit, затем аннотации JUnit, чтобы понять, откуда берутся методы @Test. Утверждения располагаются внутри этих методов.

Ментальная модель: проваленное утверждение проваливает тест

Метод JUnit-теста выполняется сверху вниз. Каждое утверждение, которое выполняется, не издаёт никаких звуков; первое, которое не выполняется, бросает AssertionFailedError — JUnit перехватывает его и фиксирует как провал, и на этом метод останавливается. Таким образом, утверждения являются точками выхода теста. Принятый порядок аргументов — ожидаемое первым, фактическое вторым, и каждый метод принимает необязательное финальное сообщение, которое используется только при неудаче проверки:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class CalculatorTest {
  @Test
  void addsTwoNumbers() {
    int result = 2 + 3;
    assertEquals(5, result, "2 + 3 should equal 5");
  }
}

Методы импортируются статически (import static ... Assertions.*), чтобы тело теста читалось как обычный текст — assertEquals(...), а не Assertions.assertEquals(...).

Равенство, истинность и нулевость

Эти четыре варианта покрывают большинство реальных утверждений:

МетодПроходит, когда
assertEquals(expected, actual)expected.equals(actual) равно true
assertNotEquals(unexpected, actual)значения не равны
assertTrue(condition) / assertFalse(condition)boolean равен true / false
assertNull(obj) / assertNotNull(obj)ссылка является / не является null
assertSame(expected, actual)оба указывают на один и тот же объект (==)
assertArrayEquals(expected, actual)массивы равны поэлементно
assertEquals("HELLO", "hello".toUpperCase());
assertTrue(List.of(1, 2, 3).contains(2), "list should contain 2");
assertNull(map.get("missing"));
assertNotSame(new String("a"), new String("a")); // distinct objects
assertArrayEquals(new int[]{1, 2, 3}, computeRange(3));

assertEquals использует .equals(), поэтому два разных объекта String с одинаковыми символами проходят проверку; assertSame использует ==, поэтому они её не пройдут. Используйте assertSame только тогда, когда именно идентичность объекта является предметом проверки.

Тестирование исключений с помощью assertThrows

Тест иногда утверждает, что код должен завершиться ошибкой — что некорректные данные вызывают исключение. assertThrows принимает тип исключения и лямбду; он проходит только если выполнение лямбды бросает этот тип (или подтип), и возвращает перехваченное исключение, чтобы вы могли проверить его сообщение:

@Test
void rejectsNullName() {
  IllegalArgumentException ex = assertThrows(
      IllegalArgumentException.class,
      () -> greet(null));
  assertEquals("name must not be null", ex.getMessage());
}

Его зеркальным отражением является assertDoesNotThrow, который проваливается, если лямбда что-либо бросает — полезно для подтверждения того, что ранее проблемный путь теперь выполняется без ошибок. (Про правила, которым должны соответствовать исключения в вашем коде, см. исключения Java.)

Группировка с помощью assertAll

По умолчанию первое проваленное утверждение завершает метод, скрывая все последующие проблемы. assertAll выполняет каждое содержащееся утверждение, даже если некоторые проваливаются, а затем сообщает обо всех неудачах вместе — идеально для проверки нескольких свойств одного объекта:

@Test
void buildsCompleteUser() {
  User u = User.of("[email protected]");
  assertAll("user",
      () -> assertEquals("[email protected]", u.email()),
      () -> assertTrue(u.isActive()),
      () -> assertNotNull(u.createdAt()));
}

Если и email, и флаг активности неправильны, один запуск сообщит вам об обоих — вместо того чтобы исправлять одно, перезапускать и обнаруживать следующее.

Рабочий пример: самопроверяющийся тестовый прогон без фреймворка

В среде выполнения кода нет JUnit в classpath, поэтому эта программа реализует ту же идею на обычном Java: небольшие вспомогательные методы assertEquals, assertTrue, assertThrows и assertAll, которые ведут себя как в Jupiter — молчат при успехе, громко сообщают при неудаче — и проверяют несколько методов под тестом, выводя в конце итог в стиле test runner. API, который вы реально бы написали, показан в статических блоках выше; здесь демонстрируется то, что эти методы делают.

java— editable, runs on the server

Что следует вынести из этого прогона:

  • Прошедшие проверки не производят никаких строк с неудачами — печатаются только add(2,3) verified == 5 и tag conditions verified, отражая правило JUnit: выполненное утверждение молчит, и только неудачи говорят.
  • assertThrows вывел caught expected IllegalArgumentException: name must not be null, показывая шаблон: лямбда должна бросить исключение, тип должен совпасть, а сообщение перехваченного исключения доступно для следующей проверки.
  • assertAll ran 3 checks, 0 failed подтверждает поведение группировки — все три лямбды выполнились и были подсчитаны вместе, что именно поэтому assertAll выявляет все проблемы за один проход, а не останавливается на первой.
  • Финальный SUMMARY -> passed: 7, failed: 0 подсчитывает семь отдельных утверждений во всей программе (одно на равенство, два булевых, одно на исключение и три внутри assertAll) — такой же подсчёт ведёт реальный test runner: каждый вызов assertX — это одна проверка.
  • Здесь не импортировался тестовый фреймворк, но структура идентична Jupiter: вспомогательные методы, которые молчат при успехе и явно сообщают при неудаче. Замена их на org.junit.jupiter.api.Assertions.* изменит только импорты, а не логику рассуждений о каждой проверке.
Внимание

Порядок аргументов поначалу путает почти всех: это assertEquals(expected, actual), а не наоборот. Если их поменять местами, тест всё равно работает, но при неудаче сообщение читается в обратном порядке — оно утверждает, что ваше правильное значение неверно, а ошибочное — верно. Всегда ставьте литеральное или заведомо правильное значение первым.

Что дальше

Утверждения — лишь один элемент тестового метода. Чтобы использовать их эффективно:

Практика

Практика
Что дает 'assertAll' в JUnit 5 по сравнению с последовательностью отдельных утверждений?
Что дает 'assertAll' в JUnit 5 по сравнению с последовательностью отдельных утверждений?
Was this page helpful?