Смешивание JUnit4 и JUnit5

JUnit — одна из самых популярных сред тестирования для языка программирования Java. Это также сыграло важную роль в разработке Test Driven Development, или TDD. В 2018 году была выпущена последняя версия этой платформы, JUnit5, и многим программистам, использующим платформу JUnit, пришлось решить, следует ли рефакторить свой существующий код, написанный в предыдущей версии JUnit.

‹tl;dr› Эта статья посвящена объединению JUnit4 и JUnit5 в ваш текущий проект, чтобы получить преимущества JUnit5 без рефакторинга всех уже написанных тестов JUnit4. Цель состоит не в том, чтобы подробно описать JUnit5, а в том, чтобы дать рекомендации по переносу поддержки JUnit5.

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

К счастью, JUnit5 имеет очень хорошую поддержку для работы бок о бок с версией 4. Думаю, это важно в проектах, где есть сотни уже написанных тестов. Рефакторинг всех из них — это огромная работа, и гораздо проще отказаться, чем продолжать внедрять что-то новое.

В следующем посте я разбил шаги, которые вам нужно будет выполнить, чтобы внедрить поддержку JUtin5 для тестов, при этом поддерживая тесты JUnit4.

Предположим, у нас есть проект gradle

plugins {
  id 'java'
  id 'com.adarshr.test-logger' version '1.6.0'
}

group 'rainoko'
version '1.0-SNAPSHOT'

sourceCompatibility = 11

repositories {
  mavenCentral()
}

dependencies {
  testCompile('junit:junit:4.12')
}

wrapper {
  gradleVersion = '5.2.1'
}

с некоторым простым классом SimpleNaturalCalculator.java

package ee.rainoko.junit5demo;

public class SimpleNaturalCalculator {
  public int addition(int arg1, int arg2) {
    return arg1 + arg2;
  }

  public int substraction(int arg1, int arg2) {
    return arg1 - arg2;
  }

  public int multiplication(int arg1, int arg2) {
    return arg1 * arg2;
  }

  public int division(int arg1, int arg2) {
    return arg1 / arg2;
  }
}

И покрыт тестом SimpleNaturalCalculatorTest.java

package ee.rainoko.junit5demo;

import org.junit.*;

import static org.junit.Assert.*;

public class SimpleNaturalCalculatorJunit4Test {
  @Test
  public void shouldAdd() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.addition(1, 2);

    assertEquals(3, result);
  }

  @Test
  public void shouldSubstract() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.substraction(1, 2);

    assertEquals(-1, result);
  }

  @Test
  public void shouldMultiply() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.multiplication(1, 2);

    assertEquals(2, result);
  }

  @Test
  public void shouldDivide() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.division(1, 2);

    assertEquals(0, result);
  }
}

Как мы видим, это тест JUnit4 с использованием аннотаций из org.junit.

Давайте запустим это с помощью следующей команды:

gradlew test

И получаем такой результат:

ee.rainoko.junit5demo.SimpleNaturalCalculatorTest
  Test shouldAdd PASSED
  Test shouldDivide PASSED
  Test shouldSubstract PASSED
  Test shouldMultiply PASSED

Сейчас мы настраиваем стандартный проект с JUnit4.

Давайте продолжим. Наша цель — внедрить поддержку JUnit5. В отличие от версии 4, версия 5 имеет модульную структуру библиотеки: [https://junit.org/junit5/docs/current/user-guide/]

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Платформа JUnit служит основой для запуска сред тестирования на JVM. [https://junit.org/junit5/docs/current/user-guide/]

JUnit Jupiter — это комбинация новой модели программирования и модели расширений для написания тестов и расширений в JUnit 5. [https://junit.org/junit5/docs/current/user- руководство/]

JUnit Vintage предоставляет TestEngine для запуска тестов на основе JUnit 3 и JUnit 4 на платформе.[https://junit.org/junit5/docs/current/user-guide/]

Теперь, зная это, давайте добавим поддержку JUnit5. Для начала, чтобы избежать путаницы, давайте переименуем наш тестовый класс junit4 в SimpleNaturalCalculatorJunit4Test.java.

Давайте добавим зависимости JUnit5 в gradle.

// JUnit Jupiter
testImplementation('org.junit.jupiter:junit-jupiter:5.4.0')

// JUnit Vintage
testCompileOnly('junit:junit:4.12')
testRuntimeOnly('org.junit.vintage:junit-vintage-engine:5.4.0') {
  because 'allows JUnit 3 and JUnit 4 tests to run'
}

Как видите, мы добавили junit-jupiter. Также отмечен junit:4.12 как testCompileOnly. Эта библиотека по-прежнему необходима для компиляции, если мы хотим использовать наши старые тесты junit4 без каких-либо изменений. А затем мы также добавили junit-vintage-engine, чтобы платформа junit могла запускать тесты JUnit4 параллельно с тестами JUnit5.

Наконец, нам нужно добавить также команду для использования платформы JUnit5 для запуска тестов в gradle.

test {
  useJUnitPlatform()
}

Нет, давайте добавим тестовый класс JUnit5SimpleNaturalCalculatorJunit5Test.java

package ee.rainoko.junit5demo;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class SimpleNaturalCalculatorJunit5Test {
  @Test
  void shouldAdd() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.addition(1, 2);

    assertEquals(3, result);
  }

  @Test
  void shouldSubstract() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.substraction(1, 2);

    assertEquals(-1, result);
  }

  @Test
  void shouldMultiply() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.multiplication(1, 2);

    assertEquals(2, result);
  }

  @Test
  void shouldDivide() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.division(1, 2);

    assertEquals(0, result);
  }
}

Как вы можете подтвердить из импорта, теперь мы используем junit jupiter. Обратите внимание, что тестовый класс защищен пакетом, как и методы. JUnit5 теперь позволяет нам использовать модификаторы package-protected, что, как мне кажется, лучше раскрывает назначение модификаторов доступа java.

Давайте запустим наши тесты

gradlew test

И у нас есть выход

ee.rainoko.junit5demo.SimpleNaturalCalculatorJunit4Test
 Test shouldAdd PASSED
 Test shouldDivide PASSED
 Test shouldSubstract PASSED
 Test shouldMultiply PASSED
ee.rainoko.junit5demo.SimpleNaturalCalculatorJunit5Test
  Test shouldAdd() PASSED
  Test shouldDivide() PASSED
  Test shouldSubstract() PASSED
  Test shouldMultiply() PASSED

Мы можем даже использовать смешанный тестовый класс.

package ee.rainoko.junit5demo;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class SimpleNaturalCalculatorJunitMixedTest {
  @org.junit.Test
  public void shouldAddJunit4() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.addition(1, 2);

    org.junit.Assert.assertEquals(3, result);
    org.junit.jupiter.api.Assertions.assertEquals(3, result);
  }

  @org.junit.jupiter.api.Test
  void shouldAddJunit5() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.addition(1, 2);

    org.junit.Assert.assertEquals(3, result);
    org.junit.jupiter.api.Assertions.assertEquals(3, result);
  }

  @org.junit.Test
  public void shouldSubstractJunit4() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.substraction(1, 2);

    assertEquals(-1, result);
  }

  @org.junit.jupiter.api.Test
  void shouldSubstractJunit5() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.substraction(1, 2);

    assertEquals(-1, result);
  }

  @org.junit.Test
  public void shouldMultiplyJunit4() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.multiplication(1, 2);

    assertEquals(2, result);
  }

  @org.junit.jupiter.api.Test
  void shouldMultiplyJunit5() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.multiplication(1, 2);

    assertEquals(2, result);
  }

  @org.junit.Test
  public void shouldDivideJunit4() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.division(1, 2);

    assertEquals(0, result);
  }

  @org.junit.jupiter.api.Test
  void shouldDivideJunit5() {
    SimpleNaturalCalculator calculator = new SimpleNaturalCalculator();

    int result = calculator.division(1, 2);

    assertEquals(0, result);
  }
}

Как видите, теперь у нас есть аннотации от org.junit и от org.junit.jupiter.api. Платформа JUnit5 выбирает правильный движок для каждого метода тестирования.

Вы даже можете смешивать утверждения из старого пакета JUnit4 и нового пакета JUnit5 в любом типе теста.

gradlew test

И получаем вывод

ee.rainoko.junit5demo.SimpleNaturalCalculatorJunit4Test
  Test shouldAdd PASSED
  Test shouldDivide PASSED
  Test shouldSubstract PASSED
  Test shouldMultiply PASSED
ee.rainoko.junit5demo.SimpleNaturalCalculatorJunitMixedTest
  Test shouldAddJunit4 PASSED
  Test shouldDivideJunit4 PASSED
  Test shouldMultiplyJunit4 PASSED
  Test shouldSubstractJunit4 PASSED
ee.rainoko.junit5demo.SimpleNaturalCalculatorJunit5Test
  Test shouldAdd() PASSED
  Test shouldDivide() PASSED
  Test shouldSubstract() PASSED
  Test shouldMultiply() PASSED
ee.rainoko.junit5demo.SimpleNaturalCalculatorJunitMixedTest
  Test shouldAddJunit5() PASSED
  Test shouldDivideJunit5() PASSED
  Test shouldMultiplyJunit5() PASSED
  Test shouldSubstractJunit5() PASSED
SUCCESS: Executed 16 tests in 1s

Учтите, что JUnit4 и JUnit5 имеют разные аннотации @Before…. Если вам нужно смешать их, добавьте обе аннотации к вашим методам setUp и tearDown.

Надеюсь, что эта статья поможет перейти на JUnit5, даже если ваш проект активно использует тесты JUnit4, и получить преимущества сейчас, а не завтра.

Читать далее

Есть много хороших примеров и статей о JUnit5. Прочтите их также