Table of contents
1.
Introduction
2.
JUnit 5
3.
JUnit Jupiter
4.
Basic Annotations
4.1.
@BeforeAll and @BeforeEach
4.2.
@DisplayName and @Disabled
4.3.
@AfterEach and @AfterAll
5.
Assertions 
6.
Assumptions
7.
Exception Testing
8.
Test Suites
9.
Dynamic Tests
10.
Frequently Asked Questions
11.
Conclusions
Last Updated: Mar 27, 2024
Easy

JUnit Jupiter

Author Toohina Barua
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

The JUnit Platform is a framework for testing frameworks that run on the Java Virtual Machine (JVM). It also defines the TestEngine API, which can be used to build a platform testing framework. For writing tests and extensions, JUnit Jupiter combines the new programming model with the extension model. The Jupiter sub-project provides a TestEngine for running Jupiter-based tests on the platform. In this article, we are going to learn about JUnit Jupiter. So let us dive in!

JUnit 5

JUnit 5 includes several different modules from three different sub-projects, unlike previous versions of JUnit. JUnit 5 includes JUnit Platform, JUnit Jupiter, and JUnit Vintage.

The JUnit Platform is a foundation for testing frameworks that run on the JVM. It also defines the TestEngine API, which can be used to create a testing framework for the platform. The platform also includes a Console Launcher for launching the platform from the command line and a JUnit Platform Suite Engine for running a custom test suite using one or more of the platform's test engines. Popular IDEs (such as IntelliJ IDEA, Eclipse, NetBeans, and Visual Studio Code) and build tools include first-class support for the JUnit Platform (see Gradle, Maven, and Ant).

In JUnit 5, JUnit Jupiter combines the new programming model with the extension model for writing tests and extensions. A TestEngine is provided by the Jupiter sub-project for running Jupiter-based tests on the platform.

JUnit Vintage comes with a TestEngine that allows you to run JUnit 3 and JUnit 4 tests on the platform. JUnit Jupiter 4.12 or later must be installed on the classpath or module path.

JUnit Jupiter

This module comprises new programming and extension models for writing tests in JUnit 5. New annotations compared to JUnit Jupiter 4 are explained in detail in the sections below.

Basic Annotations

We divided this section into the following execution groups to discuss the new annotations: before the tests, during the tests (optional), and after the tests:

@BeforeAll and @BeforeEach

Here's an example of simple code that should be run before the main test cases:

@BeforeAll
static void setup() {
    log.info("@BeforeAll: executes once before all test methods in this class");
}

@BeforeEach
void init() {
    log.info("@BeforeEach: executes before each test method in this class");
}


It's important to note that the @BeforeAll annotation requires a static method; otherwise, the code will not compile.

@DisplayName and @Disabled

Let's move on to some new test-optional methods now:

@DisplayName("Single test successful")
@Test
void testSingleSuccessTest() {
    log.info("Success");
}

@Test
@Disabled("Not implemented yet")
void testShowSomething() {
}

 

As you can see, we can use new annotations to change the display name or disable the method with a comment.

@AfterEach and @AfterAll

Finally, consider the methods associated with operations after a test has been completed:

@AfterEach
void tearDown() {
    log.info("@AfterEach: executed after each test method.");
}

@AfterAll
static void done() {
    log.info("@AfterAll: executed after all test methods.");
}

 

Please keep in mind that the @AfterAll method must also be a static method.

Assertions 

Assertions have been moved to org.junit.jupiter.api.Assertions and greatly improved. Lambdas can now be used in assertions:

@Test
void lambdaExpressions() {
    List numbers = Arrays.asList(1, 2, 3);
    assertTrue(numbers.stream()
      .mapToInt(i -> i)
      .sum() > 5, () -> "Sum should be greater than 5");
}

 

Although the example above is simple, using a lambda expression for the assertion message has the advantage of being lazy evaluated, which can save time and resources if the message construction is time-consuming.

The assertAll() can now be used to group assertions, and any failed assertions within the group will be reported with a MultipleFailuresError:

@Test
 void groupAssertions() {
     int[] numbers = {0, 1, 2, 3, 4};
     assertAll("numbers",
         () -> assertEquals(numbers[0], 1),
         () -> assertEquals(numbers[3], 3),
         () -> assertEquals(numbers[4], 1)
     );
 }

 

As a result, it's now safer to make more complex assertions because we'll be able to pinpoint the precise location of any failure.

Assumptions

When certain conditions are met, assumptions are used to run tests. This is typically used for external conditions that must be met for the test to run correctly but are unrelated to the subject of the test.

With assumeTrue(), assumeFalse(), and assumingThat(), we can declare an assumption:

@Test
void trueAssumption() {
    assumeTrue(5 > 1);
    assertEquals(5 + 2, 7);
}

@Test
void falseAssumption() {
    assumeFalse(5 < 1);
    assertEquals(5 + 2, 7);
}

@Test
void assumptionThat() {
    String someString = "Just a string";
    assumingThat(
        someString.equals("Just a string"),
        () -> assertEquals(2 + 2, 4)
    );
}

 

A TestAbortedException is thrown if an assumption fails, and the test is simply skipped.

Lambda expressions are also understood by assumptions.

Exception Testing

In JUnit 5, there are two ways to test for exceptions, both of which can be done with the assertThrows() method:

@Test
void shouldThrowException() {
    Throwable exception = assertThrows(UnsupportedOperationException.class, () -> {
      throw new UnsupportedOperationException("Not supported");
    });
    assertEquals(exception.getMessage(), "Not supported");
}

 

@Test
void assertThrowsException() {
    String str = null;
    assertThrows(IllegalArgumentException.class, () -> {
      Integer.valueOf(str);
    });
}

 

The first example verifies the details of the thrown exception, while the second example verifies the exception type.

Test Suites

To continue exploring JUnit 5's new features, we'll look at the concept of aggregating multiple test classes into a test suite so that we can run them all at once. To create test suites, JUnit Jupiter 5 provides two annotations: @SelectPackages and @SelectClasses.

Keep in mind that most IDEs don't support these features at this time.

Let's start with the first one:

@RunWith(JUnitPlatform.class)
@SelectPackages("com.baeldung")
public class AllUnitTest {}

 

When running a test suite, @SelectPackage is used to specify the names of packages to be selected. It will run all of the tests in our example. The @SelectClasses annotation is used to specify which classes should be used when running a test suite:

@RunWith(JUnitPlatform.class)
@SelectClasses({AssertionTest.class, AssumptionTest.class, ExceptionTest.class})
public class AllUnitTest {}

 

The above class, for example, will generate a suite of three test classes. Please keep in mind that the classes do not need to be in one package.

Dynamic Tests

The final topic we'll cover is JUnit 5's Dynamic Tests feature, which allows us to declare and run test cases that are generated at runtime. Dynamic Tests, in contrast to Static Tests, which define a fixed number of test cases at compile time, allow us to define test cases dynamically during runtime.

A factory method annotated with @TestFactory can generate dynamic tests. Take a look at the following code:

@TestFactory
public Stream<DynamicTest> translateDynamicTestsFromStream() {
    return in.stream()
      .map(word ->
          DynamicTest.dynamicTest("Test translate " + word, () -> {
            int id = in.indexOf(word);
            assertEquals(out.get(id), translate(word));
          })
    );
}

 

This is a very simple and easy-to-understand example. We want to use two ArrayLists, named in and out, to translate words. A Stream, Collection, Iterable, or Iterator must be returned by the factory method. We went with a Java 8 Stream in our case.

It's important to remember that @TestFactory methods can't be private or static. The number of tests is variable and is determined by the size of the ArrayList.

Check out JUnit Interview Questions here.

Frequently Asked Questions

  1. What is @test in Jupiter?
    The annotation @test is used to indicate that the method is a test method.
     
  2. What is Assertions in Jupiter?
    Assertions is a library of useful methods for asserting conditions in tests.
     
  3. What is @Disabled in Jupiter?
    The annotation @Disabled is used to indicate that a test class or method is currently disabled and should not be executed.
     
  4. What is @AfterAll in Jupiter?
    The annotation @AfterAll indicates that the annotated method should be run after all tests in the current test class.
     
  5. What is @AfterEach in Jupiter?
    The annotation @AfterEach indicates that the annotated method should be called after each of the test class's @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, and @TestTemplate methods.

Conclusions

In this article, we presented a quick overview of the changes that are coming with JUnit 5 and JUnit Jupiter. We saw basic annotations in Jupiter. 
We hope that this blog has helped you enhance your knowledge and if you would like to learn more, check out our articles on Coding Ninjas Studio. Do upvote our blog to help other ninjas grow. Happy Coding!

Live masterclass