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@Nestedtest classes. Nesting can be arbitrarily deep, and those inner classes are considered to be full members of the test class family with one exception:@BeforeAlland@AfterAllmethods do not work by default. The reason is that Java does not allowstaticmembers in inner classes. However, this restriction can be circumvented by annotating a@Nestedtest 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.
