JUnit 5 - makstron/info GitHub Wiki

JUnit 5 is the next generation of JUnit. The goal is to create an up-to-date foundation for developer-side testing on the JVM. This includes focusing on Java 8 and above, as well as enabling many different styles of testing.

Unlike previous versions of JUnit, JUnit 5 is composed of several different modules from three different sub-projects.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform serves as a foundation for launching testing frameworks on the JVM.
  • JUnit Jupiter is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. The Jupiter sub-project provides a TestEngine for running Jupiter based tests on the platform.
  • JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.

Implementation

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'

or

testImplementation(platform('org.junit:junit-bom:5.7.2'))
testImplementation('org.junit.jupiter:junit-jupiter')

Some useful info

Ctrl + Shift + T - for create new test for current class

Annotations

@Test

Denotes that a method is a test method Методы для выполнения тестов, будут вызваны при запуске теста

@BeforeEach

Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class

@AfterEach

Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class

@BeforeAll

Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class

@AfterAll

Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class

@DisplayName

Declares a custom display name for the test class or test method.

Отображаемое название теста
Значение аннотации имеет приоритет перед названием функции
Аннотация применима и к классу

@DisplayName("Override class name")
class HelloJunit5Test {

    @DisplayName("\uD83D\uDC4D")
    @Test
    fun `First test ╯°□°)╯`() {
        print("Hello, JUnit5!")
    }
}

@Disabled

Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited.

@Disabled("Disabled until bug #2019 has been fixed!")
public class DisabledClassTest {

    @Disabled("Disabled until CustomerService is up!")
    @Test
    void testCustomerServiceGet() {
        assertEquals(2, 1 + 1);
    }

}

@RepeatedTest

Число повторений теста

@RepeatedTest(10, name = "{displayName} {currentRepetition} из {totalRepetitions}")
fun `Повторяемый тест`() {

}

Assertions

Assertion'ы находятся в классе org.junit.jupiter.Assertions и являются статическими методами.

Базовые assertion'ы

@Test
fun `Base assertions`() {
    assertEquals("a", "a")
    assertEquals(2, 1 + 1, "Optional message") // последним аргументом является сообщение, выводимое в случае ошибки
    assertEquals(2, 1 + 1, { "Assertion message " + "can be lazily evaluated" }) //лямбда-выражение вычисляется только в случае неудачного прохождения теста
    assertTrue(b)
    assertEquals(1.32, 2.05 - 0.73, 0.0001) // для дробных чисел позволяет указать дельту
    assertArrayEquals({1,2,3}, {1,2,3}) // Сравнивает массивы различных типов
    assertNull(null); // passed
    assertNotNull(null); // failed
    assertSame(obj, obj2); // use equals()
    assertNotSame(obj, obj2); // use equals()
    
    fail(); // Метод, который без всякой проверки просто завершит ваш тест неудачей
}

Групповые assertion'ы

class Person(val firstName: String, val lastName: String)
import org.junit.jupiter.api.Assertions.assertAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.function.Executable

class HelloJunit5Test {

    @Test
    fun `Grouped assertions`() {
        val person = Person("John", "Doe")
        assertAll("person",
                Executable { assertEquals("John", person.firstName) },
                Executable { assertEquals("Doe", person.lastName) }
        )
    }
}

Проверка времени выполнения тестов

@Test
fun `Timeout not exceeded`() {
    // Тест упадёт после выполнения лямбда-выражения, если оно превысит 1000 мс
    // Лямбда-выражение выполняется полностью, даже когда время выполнения уже превысило допустимое
    assertTimeout(ofMillis(1000)) {
        print("Выполняется операция, которая займёт не больше 1 секунды")
        Thread.sleep(3)
    }
}
@Test
fun `Timeout not exceeded with preemptively exit`() {
    // Тест упадёт, как только время выполнения превысит 1000 мс
    assertTimeoutPreemptively(ofMillis(1000)) {
        print("Выполняется операция, которая займёт не больше 1 секунды")
        Thread.sleep(3)
    }
}

Assumptions

Assumption'ы предоставляют возможность выполнения тестов только в случае выполнения определённых условий
Предположения позволяют указать условия, при которых должен выполняться тест-кейс. Если эти условия выполнения теста не соблюдаются, тест автоматически считается успешным.

Assumptions class provides following overloaded methods.

  1. Assumptions.assumeTrue() – If the condition is true, then run the test, else aborting the test.
  2. Assumptions.false() – If the condition is false, then run the test, else aborting the test.
  3. Assumptions.assumingThat() – is much more flexible, If condition is true then executes, else do not abort test continue rest of code in test.
@Test
public void testEJB()
{
    assumeTrue(System.getProperty("java.version").startsWith("1.8"))
    // test something here
}

Extensions

The purpose of Junit 5 extensions is to extend the behavior of test classes or methods, and these can be reused for multiple tests.

JUnit 5 extensions are related to a certain event in the execution of a test, referred to as an extension point. When a certain life cycle phase is reached, the JUnit engine calls registered extensions.

Five main types of extension points can be used:

  • test instance post-processing
  • conditional test execution
  • life-cycle callbacks
  • parameter resolution
  • exception handling

TestInstancePostProcessor Extension

This type of extension is executed after an instance of a test has been created. The interface to implement is TestInstancePostProcessor which has a postProcessTestInstance() method to override.

Common use cases include injecting dependencies into the test instance, invoking custom initialization methods on the test instance, etc.

An extension that instantiates a logger object, then calls the setLogger() method on the test instance:

public class LoggingExtension implements TestInstancePostProcessor {

    @Override
    public void postProcessTestInstance(Object testInstance, 
      ExtensionContext context) throws Exception {
        Logger logger = LogManager.getLogger(testInstance.getClass());
        testInstance.getClass()
          .getMethod("setLogger", Logger.class)
          .invoke(testInstance, logger);
    }
}

Conditional Test Execution

JUnit 5 provides a type of extension that can control whether or not a test should be run. This is defined by implementing the ExecutionCondition interface.

The method verifies if a property representing the current environment name equals “qa” and disables the test in this case:

public class EnvironmentExtension implements ExecutionCondition {

    @Override
    public ConditionEvaluationResult evaluateExecutionCondition(
      ExtensionContext context) {
        
        Properties props = new Properties();
        props.load(EnvironmentExtension.class
          .getResourceAsStream("application.properties"));
        String env = props.getProperty("env");
        if ("qa".equalsIgnoreCase(env)) {
            return ConditionEvaluationResult
              .disabled("Test disabled on QA environment");
        }
        
        return ConditionEvaluationResult.enabled(
          "Test enabled on QA environment");
    }
}

Lifecycle Callbacks

This set of extensions is related to events in a test's lifecycle and can be defined by implementing the following interfaces:

  • BeforeAllCallback and AfterAllCallback – executed before and after all the test methods are executed
  • BeforeEachCallBack and AfterEachCallback – executed before and after each test method
  • BeforeTestExecutionCallback and AfterTestExecutionCallback – executed immediately before and immediately after a test method

If the test also defines its lifecycle methods, the order of execution is:

  • BeforeAllCallback
  • BeforeAll
  • BeforeEachCallback
  • BeforeEach
  • BeforeTestExecutionCallback
  • Test
  • AfterTestExecutionCallback
  • AfterEach
  • AfterEachCallback
  • AfterAll
  • AfterAllCallback

A Guide to JUnit 5 Extensions




JUnit 5 User Guide
Тестирование с помощью JUnit 5 на Kotlin
10 интересных нововведений в JUnit 5

⚠️ **GitHub.com Fallback** ⚠️