The Basics of JUnit 5 – A Preview

1. Overview

JUnit is one of the most popular unit testing frameworks for Java, so it
is a big deal in the developer community when new major versions come
out. An alpha version of JUnit 5 was released in early February, and it
contains a number of exciting innovations.

This article will explore the new features of this release, and the main
differences with previous versions.

2. Dependencies and Setup

Installing JUnit 5 is pretty straightforward. Just add the following
dependency to your pom.xml:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.1.0</version>
    <scope>test</scope>
</dependency>

However, at the moment no IDEs support JUnit 5 yet, so you will also
have to specify a build script:

<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.19.1</version>
    <dependencies>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-surefire-provider</artifactId>
            <version>1.0.2</version>
        </dependency>
    </dependencies>
</plugin>

It is important to note that this version requires Java 8 to work.

When creating a test, make sure you import org.junit.jupiter.api.Test

3. What’s New

JUnit 5 tries to take full advantage of the new features from Java 8,
especially lambda expressions.

3.1. Assertions

Assertions have been moved to org.junit.jupiter.api.Assertions and
have been improved significantly. As mentioned earlier, you can now use
lambdas in assertions:

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

Although the example above is trivial, one advantage of using the lambda
expression for the assertion message is that it is lazily evaluated,
which can save time and resources if the message construction is
expensive.

It is also now possible to group assertions with assertAll() which
will report any failed assertions within the group 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)
     );
 }

This means it is now safer to make more complex assertions, as you will
be able to pinpoint the exact location of any failure.

3.2. Assumptions

Assumptions are used to run tests only if certain conditions are met.
This is typically used for external conditions that are required for the
test to run properly, but which are not directly related to whatever is
being tested.

You can declare an assumption with assumeTrue(), assumeFalse(), and
assumingThat().

@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)
    );
}

If an assumption fails, a TestAbortedException is thrown and the test
is simply skipped.

Assumptions also understand lambda expressions.

3.3. Exceptions

JUnit 5 improves support for exceptions. An assertThrows() method has
been added that verifies that an expression throws an expression of a
given type:

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

As the example demonstrates, JUnit 5 offers more control over the thrown
exceptions than JUnit 4 used to. The most obvious implication is that it
is now possible to easily get any information we might need about the
exception, as we did in our example by inspecting the exception message.

3.4. Nested Tests

Nested tests have been added to allow developers to express complex
relationships between different groups of tests. The syntax is quite
straightforward – all you have to do is annotate an inner class with
@Nested.

The JUnit documentation offers an elaborate
example,
which illustrates one of the possible uses.

3.5. Disabling Tests

Tests can now be disabled with the @Disabled annotation.

@Test
@Disabled
void disabledTest() {
    assertTrue(false);
}

This test will not be run. The @Disabled annotation can be applied to
either a test case or a test method and is equivalent to @Ignore from
JUnit 4.

3.6. Tagging

Tags are the replacement for Categories from JUnit 4. Tags can be
applied with the @Tag annotation. These allow developers to group and
filter tests.

@Tag("Test case")
public class TaggedTest {

    @Test
    @Tag("Method")
    void testMethod() {
        assertEquals(2+2, 4);
    }
}

4. Conclusion

The write-up was a quick overview of the changes that are coming with
JUnit 5.

It is important to note that at the moment of writing only the Alpha
build was available, so some things might still change before release.

The examples used in this article can be found in the
linked
GitHub project
.

Leave a Reply

Your email address will not be published.