Каковы ключевые различия между JUnit 4 и JUnit 5?

Ответ

JUnit 5 (Jupiter) — это полная переработка фреймворка, решающая архитектурные ограничения JUnit 4.

Основные отличия:

  1. Архитектура:

    • JUnit 4: Монолитный JAR.
    • JUnit 5: Модульная платформа из трех частей: Jupiter (новый API), Vintage (запуск старых JUnit 4 тестов), Platform (запуск на разных инструментах).
  2. Аннотации и API:

    • @Test больше не имеет параметров timeout и expected. Вместо них используются утверждения:
      // JUnit 5
      assertTimeout(Duration.ofSeconds(1), () -> ...);
      assertThrows(NullPointerException.class, () -> ...);
    • @Before / @After@BeforeEach / @AfterEach.
    • @BeforeClass / @AfterClass@BeforeAll / @AfterAll (методы теперь могут быть static или instance при использовании @TestInstance(Lifecycle.PER_CLASS)).
  3. Новые возможности:

    • @DisplayName: Человекочитаемые имена для тестов и классов.
    • @Nested: Логическая группировка тестов во внутренних классах.
    • @TestFactory: Динамическое создание тестовых случаев во время выполнения.
    • @Tag: Гибкая фильтрация тестов для запуска.
    • Расширения: Мощная замена @Rule и @RunWith через @ExtendWith.

Пример теста JUnit 5:

@DisplayName("Калькулятор")
class CalculatorTest {

    @Nested
    @DisplayName("Тесты сложения")
    class AddTests {
        @Test
        @DisplayName("1 + 1 = 2")
        void shouldAddTwoNumbers() {
            assertEquals(2, Calculator.add(1, 1),
                () -> "1 + 1 должно равняться 2"); // Ленивое сообщение
        }
    }
}

Миграция: JUnit 5 обратно совместим через модуль JUnit Vintage, который позволяет запускать JUnit 4 тесты в среде JUnit 5.

Ответ 18+ 🔞

А, ну это ж про JUnit, блядь! Слушай, история такая: был у нас JUnit 4, старый добрый, но, как водится, весь в архитектурных костылях. И вот приходит JUnit 5, он же Jupiter, и такой: "Ну нахуй, ребята, всё переписываем, но по-умному!"

Чё там поменялось, ёпта:

  1. Архитектура, блядь:

    • JUnit 4: Один жирный JAR, как монолит, блядь. Всё в кучу.
    • JUnit 5: Разобрали на три модуля, как конструктор. Jupiter — это новые плюшки и API, Vintage — чтоб старые тесты JUnit 4 не выкидывать, а Platform — чтоб всякие IDE и сборщики могли с этим хозяйством работать. Умно, сука!
  2. Аннотации и API — тут просто пиздец изменений:

    • Раньше в @Test можно было timeout и expected впихнуть. Теперь — хуй! Всё через утверждения, ясень пень:
      // JUnit 5
      assertTimeout(Duration.ofSeconds(1), () -> ...); // Ждём секунду, а потом — пошёл нахуй, тест упал
      assertThrows(NullPointerException.class, () -> ...); // Ожидаем, что тут вылетит NPE, и радуемся, когда вылетает
    • @Before / @After переименовали в @BeforeEach / @AfterEach. Так понятнее, блядь, что перед КАЖДЫМ тестом.
    • @BeforeClass / @AfterClass стали @BeforeAll / @AfterAll. И внимание, ёпта — методы теперь могут быть не только static, но и обычными, если сверху класса написать @TestInstance(Lifecycle.PER_CLASS). Гибкость, мать её!
  3. Новые плюшки, от которых глаза на лоб лезут:

    • @DisplayName: Можно дать тесту человеческое имя, типа "Проверка, что сервис не сдохнет от null". Красота, а не testServiceWithNullShouldNotThrow, блядь.
    • @Nested: Группируй тесты во внутренних классах, чтоб не было одной сраной кучи методов. Порядок, сука!
    • @TestFactory: Это вообще магия. Динамически тесты генерируй прямо во время выполнения. Хули тебе ещё надо?
    • @Tag: Навешивай на тесты метки и потом запускай только "медленные" или только "интеграционные". Фильтрация — огонь.
    • Расширения: Вместо старых костылей @Rule и @RunWith теперь есть @ExtendWith. Мощь нереальная.

Вот, смотри, как теперь тесты выглядят, просто песня:

@DisplayName("Калькулятор") // Смотри какая красота, а не просто CalculatorTest
class CalculatorTest {

    @Nested
    @DisplayName("Тесты сложения") // Группочка!
    class AddTests {
        @Test
        @DisplayName("1 + 1 = 2") // Во, блядь, сразу ясно, что проверяем
        void shouldAddTwoNumbers() {
            assertEquals(2, Calculator.add(1, 1),
                () -> "1 + 1 должно равняться 2"); // Сообщение об ошибке ленивое, вычисляется только если тест упал. Умно, ёпта!
        }
    }
}

А как мигрировать, если старый код есть? Да не ссы, чувак! JUnit 5 обратно совместим через модуль JUnit Vintage. Он как переводчик для старых тестов JUnit 4 — запускает их в новой среде. Так что можно постепенно переползать, не теряя всё сразу. Удобно, блядь, как швейцарские часы!