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.
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')
Ctrl + Shift + T - for create new test for current class
Denotes that a method is a test method Методы для выполнения тестов, будут вызваны при запуске теста
Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class
Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class
Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class
Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current class
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!")
}
}
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(10, name = "{displayName} {currentRepetition} из {totalRepetitions}")
fun `Повторяемый тест`() {
}
Assertion'ы находятся в классе org.junit.jupiter.Assertions и являются статическими методами.
@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(); // Метод, который без всякой проверки просто завершит ваш тест неудачей
}
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)
}
}
Assumption'ы предоставляют возможность выполнения тестов только в случае выполнения определённых условий
Предположения позволяют указать условия, при которых должен выполняться тест-кейс. Если эти условия выполнения теста не соблюдаются, тест автоматически считается успешным.
Assumptions class provides following overloaded methods.
- Assumptions.assumeTrue() – If the condition is true, then run the test, else aborting the test.
- Assumptions.false() – If the condition is false, then run the test, else aborting the test.
- 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
}
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
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);
}
}
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");
}
}
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
JUnit 5 User Guide
Тестирование с помощью JUnit 5 на Kotlin
10 интересных нововведений в JUnit 5