Use of Commonly Used Annotations in JUnit5

  • 2021-10-16 01:46:42
  • OfStack

20 Notes to the Catalogue
Meta-annotation and combined annotation
Summary
References:

Annotation (Annotations) is the signature technology of JUnit, so this article will study its 20 annotations, meta-annotations and composite annotations.

20 notes

These annotations are defined in the package org. junit. jupiter. api as follows:

@ Test test method, which can be run directly.

@ ParameterizedTest parameterized tests, such as:


@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(StringUtils.isPalindrome(candidate));
}

@ RepeatedTest Repeat tests, such as:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}

@ TestFactory test factory, which specializes in generating test methods, such as:


import org.junit.jupiter.api.DynamicTest;

@TestFactory
Collection<DynamicTest> dynamicTestsFromCollection() {
    return Arrays.asList(
        dynamicTest("1st dynamic test", () -> assertTrue(isPalindrome("madam"))),
        dynamicTest("2nd dynamic test", () -> assertEquals(4, calculator.multiply(2, 2)))
    );
}

@ TestTemplate test templates, such as:


final List<String> fruits = Arrays.asList("apple", "banana", "lemon");

@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String fruit) {
    assertTrue(fruits.contains(fruit));
}

public class MyTestTemplateInvocationContextProvider
        implements TestTemplateInvocationContextProvider {

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
            ExtensionContext context) {

        return Stream.of(invocationContext("apple"), invocationContext("banana"));
    }
}

@ TestTemplate must register an TestTemplateInvocationContextProvider, which is used similarly to @ Test.

@ TestMethodOrder specifies the test sequence, such as:


import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo {

    @Test
    @Order(1)
    void nullValues() {
        // perform assertions against null values
    }

    @Test
    @Order(2)
    void emptyValues() {
        // perform assertions against empty values
    }

    @Test
    @Order(3)
    void validValues() {
        // perform assertions against valid values
    }

}

Whether @ TestInstance generates multiple test instances? By default, JUnit generates one instance per test method. Using this annotation, each class can only generate one instance, such as:


@TestInstance(Lifecycle.PER_CLASS)
class TestMethodDemo {

    @Test
    void test1() {
    }

    @Test
    void test2() {
    }

    @Test
    void test3() {
    }

}

@ DisplayName custom test name will be reflected in the test report, such as:


import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("A special test case")
class DisplayNameDemo {

    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

    @Test
    @DisplayName(" °-°) ")
    void testWithDisplayNameContainingSpecialCharacters() {
    }

    @Test
    @DisplayName("😱")
    void testWithDisplayNameContainingEmoji() {
    }

}

@ DisplayNameGeneration test name system 1 processing, such as:


import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.IndicativeSentencesGeneration;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class DisplayNameGeneratorDemo {

    @Nested
    @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
    class A_year_is_not_supported {

        @Test
        void if_it_is_zero() {
        }

        @DisplayName("A negative value for year is not supported by the leap year computation.")
        @ParameterizedTest(name = "For example, year {0} is not supported.")
        @ValueSource(ints = { -1, -4 })
        void if_it_is_negative(int year) {
        }

    }

    @Nested
    @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class)
    class A_year_is_a_leap_year {

        @Test
        void if_it_is_divisible_by_4_but_not_by_100() {
        }

        @ParameterizedTest(name = "Year {0} is a leap year.")
        @ValueSource(ints = { 2016, 2020, 2048 })
        void if_it_is_one_of_the_following_years(int year) {
        }

    }

}

@ BeforeEach is executed before each @ Test, @ RepeatedTest, @ ParameterizedTest, or @ TestFactory.

@ AfterEach is executed after each @ Test, @ RepeatedTest, @ ParameterizedTest, or @ TestFactory.

@ BeforeAll is executed before all @ Test, @ RepeatedTest, @ ParameterizedTest, and @ TestFactory.

@ AfterAll is executed after all @ Test, @ RepeatedTest, @ ParameterizedTest, and @ TestFactory.

@ Nested nested test, 1 class sets 1 class, refer to the above one for examples.

@ Tag labeling is equivalent to grouping, such as:


import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}

@ Disabled disables testing, such as:


import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled("Disabled until bug #99 has been fixed")
class DisabledClassDemo {

    @Test
    void testWillBeSkipped() {
    }

}
@Timeout  For test, test factory, test template, or lifecycle method If you time out, you will be considered a failure, such as: 

class TimeoutDemo {

    @BeforeEach
    @Timeout(5)
    void setUp() {
        // fails if execution time exceeds 5 seconds
    }

    @Test
    @Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
    void failsIfExecutionTimeExceeds100Milliseconds() {
        // fails if execution time exceeds 100 milliseconds
    }

}

@ ExtendWith registration extensions, such as:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
0

JUnit5 provides a standard extension mechanism to allow developers to enhance the functionality of JUnit5. JUnit5 provides a number of standard extension interfaces that third parties can directly implement to provide custom behavior.

@ RegisterExtension registers extensions through fields, such as:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
1

@ TempDir temporary directory, such as:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
2

Meta-annotation and combined annotation

JUnit Jupiter supports meta-annotations and enables custom annotations, such as custom @ Fast annotations:


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}

Use:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
4

This @ Fast annotation is also a combination annotation, which can even be combined with @ Test one step further:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
5

Just use @ FastTest:


@RepeatedTest(10)
void repeatedTest() {
    // ...
}
6

Summary

This paper introduces and demonstrates 20 main annotations of JUnit. JUnit Jupiter supports meta-annotation, which can be customized or combined with multiple annotations.

References:

https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

Overview of https://vitzhou. gitbooks. io/junit5/content/junit/extension _ model. html #


Related articles: