Hi all, in this article, we will learn the basic JUnit 5 annotations. If you are coming from JUnit 4, you will see that some of the annotations changed in JUnit 5. All core annotations are located in the org.junit.jupiter.api package in the junit-jupiter-api module. Here are the annotations and their descriptions.
Annotation | Description |
---|---|
| This annotation denotes that a method is a test method. Unlike JUnit 4’s |
| Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden. |
| Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden. |
| Denotes that a method is a test factory for dynamic tests. Such methods are inherited unless they are overridden. |
| Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited. |
| Denotes that a method is a template for test cases designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Such methods are inherited unless they are overridden. |
| Declares a custom display name for the test class or test method. Such annotations are not inherited. |
| Denotes that the annotated method should be executed before each |
| Denotes that the annotated method should be executed after each |
| Denotes that the annotated method should be executed before all |
| Denotes that the annotated method should be executed after all |
| Denotes that the annotated class is a nested, non-static test class. |
| Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level. |
| Used to disable a test class or test method; analogous to JUnit 4’s |
| Used to register custom extensions. Such annotations are inherited. |
JUnit 5 Annotations Examples
Let’s write some example test codes for the JUnit 5 annotations.
First, we need to add JUnit 5 dependency in our pom.xml.
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.3.1</version> <scope>test</scope> </dependency>
@Test Annotation
class JUnit5TestAnnotationTest{ @Test void myTestAnnotationTest() { assertEquals(4, 2 + 2); } }
@ParameterizedTest
@ParameterizedTest @ValueSource(strings = { "Jack", "Jane", "Michael" }) void genderByNameTest(String name) { assertTrue(isMan(name)); }
@RepeatTest
@RepeatedTest(10) void repeatedTest() { // Repeats 10 times. } @RepeatedTest(3) void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) { assertEquals(3, repetitionInfo.getTotalRepetitions()); } @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}") @DisplayName("My Repeat Test!") void customRepeatTestDisplayName(TestInfo testInfo) { assertEquals(testInfo.getDisplayName(), "Repeat! 1/1"); }
@DisplayName
@DisplayName("My Test Class Name") class DisplayNameTest { @Test @DisplayName("My Test Name") void testWithDisplayName() { } }
@BeforeEach & @AfterEach
class MyTestClass{ @BeforeEach void setup() { log.info("@BeforeEach executes before each test method in this class."); } @Test void myTestMethod() { } @AfterEach void tearDown() { log.info("@AfterEach executes after each test method in this class."); } }
@BeforeAll & @AfterAll
class MyTestClass{ @BeforeAll static void initAll() { log.info("@BeforeAll executes once before all test methods in this class."); } @Test void myTestMethod() { } @AfterAll static void tearDownAll() { log.info("@AfterAll executes once after all test methods in this class."); } }
@Nested
Nested annotation denotes that the annotated class is nested, it is a non-static test class. @BeforeAll and @AfterAll methods cannot be used directly in a @Nested test class. It gives us more capabilities to declare the connection between several test groups.
@DisplayName("A stack") class TestingAStackDemo { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew() { new Stack<>(); } @Nested @DisplayName("when new") class WhenNew { @BeforeEach void createNewStack() { stack = new Stack<>(); } @Test @DisplayName("is empty") void isEmpty() { assertTrue(stack.isEmpty()); } @Test @DisplayName("throws EmptyStackException when popped") void throwsExceptionWhenPopped() { assertThrows(EmptyStackException.class, () -> stack.pop()); } @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { assertThrows(EmptyStackException.class, () -> stack.peek()); } @Nested @DisplayName("after pushing an element") class AfterPushing { String anElement = "an element"; @BeforeEach void pushAnElement() { stack.push(anElement); } @Test @DisplayName("it is no longer empty") void isNotEmpty() { assertFalse(stack.isEmpty()); } @Test @DisplayName("returns the element when popped and is empty") void returnElementWhenPopped() { assertEquals(anElement, stack.pop()); assertTrue(stack.isEmpty()); } @Test @DisplayName("returns the element when peeked but remains not empty") void returnElementWhenPeeked() { assertEquals(anElement, stack.peek()); assertFalse(stack.isEmpty()); } } }
Ref: https://junit.org/junit5/docs/current/user-guide/#writing-tests-nested
Only non-static nested classes (i.e. inner classes) can serve as@Nested
test classes. Nesting can be arbitrarily deep, and those inner classes are considered to be full members of the test class family with one exception:@BeforeAll
and@AfterAll
methods do not work by default. The reason is that Java does not allowstatic
members in inner classes. However, this restriction can be circumvented by annotating a@Nested
test class with@TestInstance(Lifecycle.PER_CLASS)
(see Test Instance Lifecycle).
@Tag
With this annotation, we can give a tag to tests for filtering them. We can do this either at the class or method level.
@Tag("Smoke") @Tag("Critical") class MySampleTagTestClass { @Test @Tag("basket") void addToBasketTest() { } }
@Disabled
This annotation is used to disable a test class or test method.
//Dısable a Test Class @Disabled class DisabledClassDemo { @Test void testWillBeSkipped() { } } //Disable a test method class DisabledTestsDemo { @Disabled @Test void testWillBeSkipped() { } @Test void testWillBeExecuted() { } }
@ExtendWith
This annotation is used to extend the capabilities of JUnit 5. You can create listeners and so on with extensions. It is like a new Rules of JUnit. Usage of a custom extension is shown below.
@ExtendWith(MyExtension.class) @Test void test() { // ... }
@TestFactory
We can use this annotation for Dynamic Tests. Dynamic tests are generated at runtime by a factory method and it is annotated with @TestFactory. Below example is gathered from the original JUnit website for more details please refer here.
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.ThrowingConsumer; class DynamicTestsDemo { // This will result in a JUnitException! @TestFactory List<String> dynamicTestsWithInvalidReturnType() { return Arrays.asList("Hello"); } @TestFactory Collection<DynamicTest> dynamicTestsFromCollection() { return Arrays.asList( dynamicTest("1st dynamic test", () -> assertTrue(true)), dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2)) ); } @TestFactory Iterable<DynamicTest> dynamicTestsFromIterable() { return Arrays.asList( dynamicTest("3rd dynamic test", () -> assertTrue(true)), dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2)) ); } @TestFactory Iterator<DynamicTest> dynamicTestsFromIterator() { return Arrays.asList( dynamicTest("5th dynamic test", () -> assertTrue(true)), dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2)) ).iterator(); } @TestFactory DynamicTest[] dynamicTestsFromArray() { return new DynamicTest[] { dynamicTest("7th dynamic test", () -> assertTrue(true)), dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2)) }; } @TestFactory Stream<DynamicTest> dynamicTestsFromStream() { return Stream.of("A", "B", "C") .map(str -> dynamicTest("test" + str, () -> { /* ... */ })); } @TestFactory Stream<DynamicTest> dynamicTestsFromIntStream() { // Generates tests for the first 10 even integers. return IntStream.iterate(0, n -> n + 2).limit(10) .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0))); } @TestFactory Stream<DynamicTest> generateRandomNumberOfTests() { // Generates random positive integers between 0 and 100 until // a number evenly divisible by 7 is encountered. Iterator<Integer> inputGenerator = new Iterator<>() { Random random = new Random(); int current; @Override public boolean hasNext() { current = random.nextInt(100); return current % 7 != 0; } @Override public Integer next() { return current; } }; // Generates display names like: input:5, input:37, input:85, etc. Function<Integer, String> displayNameGenerator = (input) -> "input:" + input; // Executes tests based on the current input value. ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0); // Returns a stream of dynamic tests. return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor); } @TestFactory Stream<DynamicNode> dynamicTestsWithContainers() { return Stream.of("A", "B", "C") .map(input -> dynamicContainer("Container " + input, Stream.of( dynamicTest("not null", () -> assertNotNull(input)), dynamicContainer("properties", Stream.of( dynamicTest("length > 0", () -> assertTrue(input.length() > 0)), dynamicTest("not empty", () -> assertFalse(input.isEmpty())) )) ))); } }
For @TestInstance and @TestTemplate you can get information at official JUnit 5 documentation page.
Thanks.
Onur Baskirt

Onur Baskirt is a Software Engineering Leader with international experience in world-class companies. Now, he is a Software Engineering Lead at Emirates Airlines in Dubai.