JUnit 5 Basics

Junit 5 Basics

JUnit 5

JUnit 5 is composed of several different modules from three different sub-projects

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

The JUnit Platform serves as a foundation for launching testing frameworks on the JVM.

JUnit Jupiter is for writing tests in JUnit 5.

JUnit Vintage is for writing tests in Junit 3 or Junit4.

Maven Dependency

Junit-jupiter-engine contains all core annotations for Junit 5

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

You add junit-vintage-engine if you want to run Junit 3 or Junit4 tests. junit-vintage-engine. transitively pulls in  junit:junit:4.12

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

Basic Junit 5 Test

Most of the Junit 5 Annotations are similar to JUnit4 but with a new package. Commonly used annotations like @Test is now in org.junit.jupiter.api package.

@Testis used to signal that the annotated method is a test method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

class SimpleTest {
@Test
void successTest() {
assertEquals("Abc", "Abc");
}

@Test
void failTest() {
fail("fail this test case");
}
}

Lifecycle Methods

Junit 5 provides the following lifecycle methods. @BeforeAll, @AfterAll@BeforeEach, and @AfterEach.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@BeforeAll
static void initAll() {
}

@BeforeEach
void init() {
}

@AfterEach
void tearDown() {
}

@AfterAll
static void tearDownAll() {
}

In Junit 4, methods annotated with @Before is executed before each test. This is equivalent to @BeforeEach in Junit 5.

Also in Junit 4, methods annotated with @BeforeClass is executed once before all tests. This is equivalent to @BeforeAll in Junit 5.

Display Name

Display name will appear in test report or IDE test runner

1
2
3
4
5
6
7
8
9
10
11
@Test
@DisplayName("A success test case")
void successTest() {
assertEquals("Abc", "Abc");
}

@Test
@DisplayName("A fail test case")
void failTest() {
fail("fail this test case");
}

Assertions

Assertion annotations are in the org.junit.jupiter.api.Assertion class. You can use static import all the static methods.

1
2
3
4
5
6
7
8
9
10
11
12
import static org.junit.jupiter.api.Assertions.*;

@Test
void testAssertions() {
assertEquals(2, 1+1);
assertEquals(2, 1+1,"Optional message here...");
assertTrue(1 < 2, () -> "Optional message here...");
assertNotEquals(1, 2);
assertNotNull("Hello World");
assertNull(null);
fail("This is a failed test");
}

Assert Exception

Test Exception - use assertThrows method to test if an Exception is being thrown. assertThrows returns the exception that was thrown. You can then verify the exception message.

1
2
3
4
5
6
7
8
9
@Test
void testException() {
Exception exception = assertThrows(ArrayIndexOutOfBoundsException.class, () ->
{
int[] myArray = {0, 1};
int thirdElemenet = myArray[2];
});
assertEquals("Index 2 out of bounds for length 2", exception.getMessage());
}

Disabled

You can disable a test class or test case by adding @Disabled annotation.

1
2
3
4
@Test
@Disabled("Disabled untile Bug-123 is fixed")
void successTest() {
}

Repeated Test

Use @RepeatedTest annotation to repeat a test a specified number of times

1
2
3
4
@RepeatedTest(10)
void repeatedTest() {
assertEquals(2, 1+1);
}

Parameterized Test

You need to add junit-jupiter-params dependency in order to run parameterized test

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

@ParameterizedTest is used to signal that the annotated method is a parameterized test
 method. you must declare at least one source that will provide the arguments for each invocation. @ValueSource specifies a list of values as argument.

1
2
3
4
5
6
7
8
9
10
11
@ParameterizedTest
@ValueSource(strings = {"ab", "bz", "ts"})
void palindromes(String argument) {
assertTrue(StringUtils.isAllLowerCase(argument));
}

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
assertTrue(argument > 0 && argument < 4);
}

The following values are supported by @ValueSource

  • short
  • byte
  • int
  • long
  • float
  • double
  • char
  • boolean
  • java.lang.String

@ValueSource only allows you to provide values for one parameter. If the method has multiple parameters, you can use @CvsSource to provide comma-separated values to the corresponding parameters. Each string represent an invocation of the parameterized test.

1
2
3
4
5
6
7
8
9
10
11
@ParameterizedTest
@CsvSource({
"apple, 1",
"banana, 2",
"'lemon, lime', 0xF1",
"strawberry, 700_000"
})
void testWithCsvSource(String fruit, int rank) {
assertNotNull(fruit);
assertNotEquals(0, rank);
}

There are other useful sources to be used besides @ValueSource and @CvsSource. Please read the JUnit 5 Documentation to learn the others.

Migration to JUnit 5

See migration tips: https://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4-tips

  • Annotations reside in the org.junit.jupiter.api package
  • Assertions reside in org.junit.jupiter.api.Assertions package
  • Assumptions reside in org.junit.jupiter.api.Assumptions package
  • @Before and @After no longer exist; use @BeforeEach and @AfterEach instead.
  • @BeforeClass and @AfterClass no longer exist; use @BeforeAll and @AfterAll instead.
  • @Ignore no longer exists: use @Disabled
  • @Category no longer exists; use @Tag instead.
  • @RunWith no longer exists; superseded by @ExtendWith.
  • @Rule and @ClassRule no longer exist; superseded by @ExtendWith and @RegisterExtension.
  • @Test(expected = …​) and the ExpectedException rule no longer exist; use Assertions.assertThrows(…​) instead.
  • Assertions and assumptions in JUnit Jupiter accept the failure message as their last argument instead of the first one.

Reference