4 minute read

Don’t tell people your plans. Just show them your results!

JUnit5 (Jupiter)

As you know, Junit 5 with the goal of supporting new features in Java 8 and above, as well as enabling many different styles of testing.

Components of Jupiter

Unlike previous versions of JUnit, JUnit 5 is composed of several different modules from three different sub-projects.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Junit Platform

The JUnit Platform serves as a foundation for launching testing frameworks on the JVM. It also defines the TestEngine API for developing a testing framework that runs on the platform.

JUnit Jupiter

is the combination of the new programming model and extension model for writing tests and extensions in JUnit 5. The Jupiter sub-project provides a TestEngine for running Jupiter based tests on the platform.

JUnit Vintage

This is mainly for backwards compatibility, it provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform. It requires JUnit 4.12 or later to be present on the class path or module path.

Talk is cheap, let’s see some code

Sample Jupiter test

class MyFirstJUnitJupiterTests {
 
    private final Calculator calculator = new Calculator();
 
    @Test
    void addition() {
        assertEquals(2, calculator.add(1, 1));
    }
 
}

@ParameterizedTest

@ParameterizedTest
    @ValueSource(strings={"hello","world","keyboard"})
    void dummyMethod1(String input) {
        assertEquals(5,inst.dummyMethod1(input));
    }

@RepeatedTest

    @RepeatedTest(5)
    void dummyTets2(){
        assertEquals(3,inst.dummyMethod1("abc"));
    }

So you’ll see those results in detailed lines, as below screenshot:

Class and method visibility

Test classes, test methods, and lifecycle methods are not required to be public, but they must not be private.

It is generally recommended to omit the public modifier for test classes, test methods, and lifecycle methods unless there is a technical reason for doing so

Annotations: @Test, @Testable , and @TestFactory

@Testable

It is used to signal to IDEs and tooling vendors that the annotated or meta-annotated element is testable.

Motivation for @Testable

Some clients of the JUnit Platform, notably IDEs such as IntelliJ IDEA, operate only on sources for test discovery. Thus, they cannot use the full runtime discovery mechanism of the JUnit Platform since it relies on compiled classes. @Testable therefore serves as an alternative mechanism for IDEs to discover potential tests by analyzing the source code only. Common Use Cases

TestEngine

public interface TestEngine A TestEngine facilitates discovery and execution of tests for a particular programming model.

For example, JUnit provides a TestEngine that discovers and executes tests written using the JUnit Jupiter programming model.

In order to facilitate test discovery within IDEs and tools prior to launching the JUnit Platform, TestEngine implementations are encouraged to make use of the @Testable annotation. For example, the @Test and @TestFactory annotations in JUnit Jupiter are meta-annotated with @Testable.

Differences among junit 4 and Jupiter

For check there are differecnes among junit 4 and Jupiter

Assert Assertions

A set of assertion methods useful for writing tests. Assertions is a collection of utility methods that support asserting conditions in tests. From Assert.assertEquals(String message, Object expected, Object actual) to Assertions.assertEquals(Object expected, Object actual, String message).

Assertions

we 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");
}

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

It’s 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)
     );
 }

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 aren’t directly related to whatever is being tested.

We can declare an assumption with assumeTrue(), assumeFalse(), and assumingThat():

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

Assumptions also understand lambda expressions.

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

Exception Testing


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

Better assertions, using matchers.

Hamcrest (For matchers)

Do you know, Hamcrest is coined by rearrange characters of Matchers

There are rich and more readable API for matchers of Hamcrest.

The error message of Hamcrest is more understandable. You can get why your test fails easily. However, the error message of assertTrue is hard to understand.

@Test
public void shouldGetAvailableTaxes() throws Exception {
    List actual = TaxCalculator.AVAILABLE_TAXES;
    //JUnit assertions
    assertEquals(Arrays.asList(KDV, OTV, MTV), actual);
    assertTrue(actual.contains(KDV));
    assertTrue(!actual.contains(KDM));
    assertEquals(3, actual.size());
 
    //Hamcrest matcher
    assertThat(actual, containsInAnyOrder(KDV, OTV, MTV));
    assertThat(actual, not(contains(KDV, MTV, OTV)));
    assertThat(actual, contains(KDV, OTV, MTV));
    assertThat(actual, hasItems(KDV, OTV));
    assertThat(actual, hasItem(MTV));
    assertThat(actual, hasSize(3));
    assertThat(actual, instanceOf(List.class));
    assertThat(actual, everyItem(notNullValue(String.class)));
    assertThat(actual, everyItem(not(isEmptyOrNullString())));
}

To use it, you must add following to pom.xml

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-library</artifactId>
  <version>2.2</version>
  <scope>test</scope>
</dependency>

–End–

Updated: