Unit Test with JUnit 5 in Java Unit Test with JUnit 5 in Java

Page content

In this tutorial, we’ll learn how to write efficient Junit test cases in Java using Junit 5 ( Jupiter ) assertions.

Junit 5

Junit is the most popular unit-testing framework in Java. Junit 5 provides many different styles of testing and focuses on Java 8 and above.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Where,

  • The Junit Platform provides the foundation for running tests on the JVM. Popular IDEs like IntelliJ, Eclipse, and Visual Studio Code and build tools like Maven, Gradle provide first-hand support for the Junit platform.
  • The Junit Jupiter provides annotations like @Test, @BeforeEach and assertions like assertEquals() to write test cases
  • The Junit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform

Setup Junit Jupiter

Import the required junit-jupiter latest dependency for Java using Maven (pom.xml) or Gradle (build.gradle), whichever you are using:-

<!-- add dependencies in pom.xml -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.junit</groupId>
            <artifactId>junit-bom</artifactId>
            <version>5.9.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
// add dependencies and test in build.gradle
dependencies {
	testImplementation(platform('org.junit:junit-bom:5.9.1'))
	testImplementation('org.junit.jupiter:junit-jupiter')
}

test {
	useJUnitPlatform()
	testLogging {
		events "passed", "skipped", "failed"
	}
}

Import Junit Jupiter

Once you add the Junit Jupiter dependency, you can import static assertions method in the unit test class like this:-

import static org.junit.jupiter.api.Assertions.*;

The Junit Annotations reside in org.junit.jupiter.api package, which you can import like this:-

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;

We are ready to write unit test cases with Junit 5.

Using Junit Assertions

See the complete list of assertions available in org.junit.jupiter.api.Assertions Java documentation.

assertNull

The assertNull assertion can be used to test the null-ness of any type of Object.

Assert that an Object is null:-

@Test
public void givenObject_whenNull_thenPass() {
    String text = null;
    Integer number = null;
    Double amount = null;
    Boolean flag = null;
    LocalDate date = null;
    List list = null;
    Map map = null;
    Character[] chars = null;

    assertNull(text);
    assertNull(number);
    assertNull(amount);
    assertNull(flag);
    assertNull(date);
    assertNull(list);
    assertNull(map);
    assertNull(chars);
}

assertNotNull

The assertNotNull assertion can be used to test the non-null value of any type of Object.

Assert that an Object is not null:-

@Test
public void givenObject_whenNotNull_thenPass() {
    String text = "foo";
    Integer number = 1;
    Double amount = 100.5;
    Boolean flag = true;
    LocalDate date = LocalDate.now();
    List list = Arrays.asList();
    Map map = Collections.emptyMap();
    Character[] chars = new Character[2];

    assertNotNull(text);
    assertNotNull(number);
    assertNotNull(amount);
    assertNotNull(flag);
    assertNotNull(date);
    assertNotNull(list);
    assertNotNull(map);
    assertNotNull(chars);
}

assertEquals

The assertEquals(expected, actual) assertion is used to test the expected value of any type e.g. Byte, Character, Double, Float, Integer, Long, Short, Object, and Collection like List and Map. The test is passed when expected.equals(actual) is true.

Assert that given expected and actual Objects are equal:-

@Test
public void givenTwoObjects_whenEqual_thenPass() {
    String text = "foo";
    Integer number = 1;
    Double amount = 100.5;
    Boolean flag = true;
    LocalDate date = LocalDate.now();
    List list = Arrays.asList("a", "b");
    Map map = Map.of("key1", "value1");

    // assertEquals(expected, actual)
    assertEquals("foo", text);
    assertEquals(1, number);
    assertEquals(100.5, amount);
    assertEquals(true, flag);
    assertEquals(LocalDate.now(), date);
    assertEquals(Arrays.asList("a", "b"), list);
    assertEquals(Map.of("key1", "value1"), map);
}

💡 Please note that assertEquals doesn’t work with Array and we should use assertArrayEquals to check the expected value of Array.


assertArrayEquals

The assertArrayEquals assertion is used to test that the Array contains the expected values in the given order. The Array can be of type boolean, byte, char, double, float, int, long, short, and Object.

Assert that expected and actual Array items are equal:-

@Test
public void givenTwoArrays_whenEqual_thenPass() {
    Character[] chars = new Character[] {'a', 'b'};
    String[] strings = new String[] {"foo", "bar"};
    Integer[] numbers = new Integer[] {1, 2};
    Boolean[] flags = new Boolean[] {true, false};

    // assertArrayEquals(expected, actual)
    assertArrayEquals(new Character[] {'a', 'b'}, chars);
    assertArrayEquals(new String[] { "foo", "bar"}, strings);
    assertArrayEquals(new Integer[] {1, 2}, numbers);
    assertArrayEquals(new Boolean[] {true, false}, flags);
}

assertIterableEquals

The assertIterableEquals asserts the deep equality of any two iterables like ArrayList, LinkedList, and LinkedHashSet. Iterables are considered equal when their iterators return equal elements in the same order as each other.

Assert that two iterables of type LinkedList and LinkedHashSet have all the given elements in the same order:-

@Test
void givenTwoIterables_whenItemsAreEqualAndSameOrder_thenPass() {
    List<String> list1 = new LinkedList<>(Arrays.asList("foo", "bar", "baz"));
    Set<String> list2 = new LinkedHashSet<>(Arrays.asList("foo", "bar", "baz"));

    assertIterableEquals(list1, list2);
}

assertLinesMatch

The assertLinesMatch assert differs from other assertions that effectively only check String.equals(Object), in that it uses the following staged matching algorithm:-

For each pair of expected and actual lines do -

  1. check if expected.equals(actual) - if yes, continue with the next pair
  2. otherwise treat expected as a regular expression and check via String.matches(String) - if yes, continue with the next pair
  3. otherwise check if the expected line is a fast-forward marker, if yes then apply fast-forward and repeat the algorithm from step 1

The assertLineMatch assertion is quite useful to test the expected log lines, command-line output, and exception stack trace like this:-

> ls -la 
  total 1 
  drwxr-xr-x 0 root 512 Jan 1 1970
@Test
void whenAssertingEqualityListOfStrings_thenEqual() {
    List<String> expected = Arrays.asList("ls -la", "total \\d+", "drwxr-xr-x \\d+ root \\d+ Jan 1 1970");
    List<String> actual = Arrays.asList("ls -la", "total 1", "drwxr-xr-x 0 root 512 Jan 1 1970");

    assertLinesMatch(expected, actual);
}

assertNotEquals

The assertNotEquals(unexpected, actual) assertion is used to test the unexpected value of any type e.g. Byte, Character, Double, Float, Integer, Long, Short, Object, and Collection like List and Map. The test is passed when unexpected.equals(actual) is false.

Assert that the given two Objects are not equal:-

@Test
public void givenTwoObjects_whenNotEqual_thenPass() {
    String text = "foo";
    Integer number = 1;
    Double amount = 100.5;
    Boolean flag = true;
    LocalDate date = LocalDate.now();
    List list = Arrays.asList("a", "b");
    Map map = Map.of("key1", "value1");

    // assertNotEquals(unexpected, actual)
    assertNotEquals("bar", text);
    assertNotEquals(2, number);
    assertNotEquals(1.5, amount);
    assertNotEquals(false, flag);
    assertNotEquals(LocalDate.now().plusDays(1), date);
    assertNotEquals(Arrays.asList("c", "d"), list);
    assertNotEquals(Map.of("key2", "value2"), map);
}

assertSame

The assertSame(expected, actual) assertion is used to test the expected and actual refer to the same object.

Assert that the given two Objects refer to the same instance:-

@Test
public void givenObject_whenSame_thenPass() {
    String text = "foo";
    Integer number = 1;
    Double amount = 100.5;
    Boolean flag = true;
    LocalDate date = LocalDate.now();
    List list = Arrays.asList("a", "b");
    Map map = Map.of("key1", "value1");

    String sameAsText = text;
    Integer sameAsNumber = number;
    Double sameAsAmount = amount;
    Boolean sameAsFlag = flag;
    LocalDate sameAsDate = date;
    List sameAsList = list;
    Map sameAsMap = map;

    // assertSame(expected, actual)
    assertSame(sameAsText, text);
    assertSame(sameAsNumber, number);
    assertSame(sameAsAmount, amount);
    assertSame(sameAsFlag, flag);
    assertSame(sameAsDate, date);
    assertSame(sameAsList, list);
    assertSame(sameAsMap, map);
}

assertNotSame

The assertNotSame(expected, actual) assertion is quite tricky.

  1. For primitives like int, double, and boolean - assertNotSame is used to test that expected and actual values are not equal
  2. For objects like String, LocalDate, List, and Map - assertNotSame is used to test that expected and actual objects do not refer to same instance
@Test
public void givenObject_whenNotSame_thenPass() {
    Integer number = 1;
    Double amount = 100.5;
    Boolean flag = true;
    String text = "foo";
    LocalDate date = LocalDate.now();
    List list = Arrays.asList("a", "b");
    Map map = Map.of("key1", "value1");

    // assertNotSame(expected, actual)
    
    // assert that two primitives are not equals
    assertNotSame(2, number);
    assertNotSame(1.5, amount);
    assertNotSame(false, flag);

    // assert that two objects are equal but do not refer to same instance
    assertNotSame(new String("foo"), text);
    assertNotSame(LocalDate.now(), date);
    assertNotSame(Arrays.asList("a", "b"), list);
    assertNotSame(Map.of("key1", "value1"), map);
}

assertTrue / assertFalse

The assertTrue(condition) and assertFalse(condition) assertions can be used to test a variety of conditions that should be true or false. Let’s look at examples, where we can use these assertions:-

  1. Assert that two Strings are equal ignoring case:-

    @Test
    public void givenTwoStrings_whenEqualIgnoringCase_thenPass(){
        String text1 = "foo";
        String text2 = "FOO";
        assertTrue(text1.equalsIgnoreCase(text2));
    }
    
  2. Assert that two Arrays are equal:-

    @Test
    public void givenTwoArrays_whenEqual_thenPass(){
        String[] array1 = new String[] {"a", "b"};
        String[] array2 = new String[] {"a", "b"};
        assertTrue(Arrays.equals(array1, array2));
    }
    
  3. Assert that two Arrays are NOT equal:-

    @Test
    public void givenTwoArrays_whenNotEqual_thenPass(){
        String[] array1 = new String[] {"a", "b"};
        String[] array2 = new String[] {"c", "d"};
        assertFalse(Arrays.equals(array1, array2));
    }
    
  4. Assert that a List contains the given value:-

    @Test
    public void givenList_whenContainsGivenValue_thenPass() {
        List<String> list = Arrays.asList("lord", "of", "the", "rings");
        assertTrue(list.contains("lord"));
    }
    
  5. Assert that a Map contains the given key, value or key-value pair (Entry):-

    @Test
    public void givenMap_whenHasGivenKeyValueOrPair_thenPass() {
        Map<String, String> myMap = Map.of("myKey", "myValue");
        //contains key
        assertTrue(myMap.containsKey("myKey"));
        //contains value
        assertTrue(myMap.containsValue("myValue"));
        //contains key-value pair
        assertTrue(myMap.containsKey("myKey") && myMap.get("myKey").equals("myValue"));
    }
    
  6. Assert that a Number is positive:-

    @Test
    public void givenNumber_whenPositive_thenPass() {
        Integer number = 2;
        assertTrue(number >= 0);
    }
    

assertInstanceOf

The assertInstanceOf(expectedType, object) assertion is used to test that the given object is an instance of expected class type:-

@Test
public void givenObject_whenInstanceOfClassType_thenPass(){
    User user = User.builder().build();
    assertInstanceOf(User.class, user);

    //assertInstanceOf(expectedType, object);
    assertInstanceOf(String.class, "text");
    assertInstanceOf(Integer.class, 1);
    assertInstanceOf(Boolean.class, true);
    assertInstanceOf(List.class, Arrays.asList());
    assertInstanceOf(Collection.class, Arrays.asList());
}

assertThrows

The assertThrows assertion is used to test the exception, which is expected to be thrown by a Java program or method execution. Let’s look at some examples:-

  1. Test that when a given String can not be parsed to an Integer, then expect IllegalArgumentException:-
    @Test
    void parseInvalidInt_whenThrowIllegalArgumentException_thenPass() {
        String str = "foo";
        assertThrows(IllegalArgumentException.class, () -> {
            Integer.valueOf(str);
        });
    }
    
  2. Test that when trying to access the invalid index of an Array, then expect IndexOutOfBoundsException:-
    @Test
    public void getInvalidIndex_whenThrowIndexOutOfBoundsException_thenPass() {
        Exception e = assertThrows(IndexOutOfBoundsException.class, () -> {
            List<String> list = Arrays.asList("foo", "bar");
            String s = list.get(2);
        });
        assertEquals("Index 2 out of bounds for length 2", e.getMessage());
    }
    
    💡The assertThrows assertion returns the thrown Exception, which can be further used to assert an error message like in the above example.

assertThrowsExactly

The assertThrowsExactly assertion is different from assertThrows where it matches the exact exception type.

assertThrows vs assertThrowsExactly

The assertThrows assertion pass when the thrown exception is an instance of (child of) a given expected exception.

For example, assertThrows pass when code throws NullPointerException since it is a child of RuntimeException:-

@Test
public void accessNull_whenThrowRuntimeException_thenPass() {
    String str = null;
    Exception e = assertThrows(RuntimeException.class, () -> {
        str.equals("foo");
    });
}

The assertThrowsExactly assertion pass when the thrown exception exactly match a given expected exception.

For example, assertThrowsExactly pass when code throws NullPointerException, since it is expecting the exact NullPointerException to be thrown:-

@Test
public void accessNull_whenThrowNullPointerException_thenPass() {
    String str = null;
    Exception e = assertThrowsExactly(NullPointerException.class, () -> {
        str.equals("foo");
    });
}

assertDoesNotThrow

The assertDoesNotThrow assertion is used to test that a supplier method executed without any exception. This method is also useful to write test cases for methods that return nothing i.e. void.

@Test
void voidMethod_whenRunWithoutException_thenPass() {
    assertDoesNotThrow(() -> {});
}

assertTimeout

The assertTimeout assertion is used to test that the execution of the supplied method completes before the given time. Let’s look at the examples:-

  1. The test pass when the supplier method or network call finishes within the given duration:-
    @Test
    public void networkCall_whenFinishInGivenTime_thenPass(){
        assertTimeout(Duration.ofSeconds(5), () -> networkCall());
    }
    
    public void networkCall() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
    }
    
  2. The test fails when the supplier method or network call takes longer than expected given duration:-
    @Test
    public void networkCall_whenTakesLongerThanExpected_thenFail(){
        assertTimeout(Duration.ofSeconds(5), () -> delayedNetworkCall());
        // fail - execution exceeded timeout of 5000 ms by 2000 ms
    }
    
    public void delayedNetworkCall() throws InterruptedException {
        TimeUnit.SECONDS.sleep(7);
    }
    
    💡The assertTimeout completes the supplier method execution even if it takes longer than the expected timeout duration and also tells you by how much time it is exceeded in the failure message.

assertTimeoutPreemptively

The assertTimeoutPreemptively assertion is different from assertTimeout, where it aborts the supplier method execution if it is taking longer than the expected timeout duration.

Let’s look at the example, where the test fails when the executable method takes longer than expected and aborts:-

@Test
public void networkCall_whenTakesLongerThanExpected_thenFail(){
    assertTimeoutPreemptively(Duration.ofSeconds(5), () -> delayedNetworkCall());
    // FAIL - execution timed out after 5000 ms
}

The failure message doesn’t show by how much time it exceeded. If you would like to print that in a failure message then use assertTimeout instead.


assertAll

The assertAll assertion should be used to group the assertions that belong together. The interesting fact about assertAll is that it tests all the grouped assertions, no matter if some of them failed and tells you the total number of failures in the test result.

Let’s look at the example:-

@Test
void givenEmail_whenValid_thenPass() {
    String email = "admin@gmail.com";
    assertAll("Should be a valid email",
            () -> assertNotNull(email),
            () -> assertTrue(email.contains("@")),
            () -> assertTrue(email.endsWith(".com")));
}

Assertion Failure Message

When an assertion fails, Junit provides a default failure message with expected and actual parameters. Junit also provides additional methods for each assertion to provide the custom error message as a string or as a supplier method.

For example, the assertTrue assertion provides the following methods:-

assertTrue(boolean condition) // Junit default error message
assertTrue(boolean condition, String message) // Custom error message
assertTrue(boolean condition, Supplier<String> messageSupplier) // Custom error messageSupplier

Custom Error Message

assertTrue(boolean condition, String message)
We can pass the custom error message as a String in the last parameter of the invoked assertion method.

@Test
void givenCondition_whenNotTrue_thenFailWithCustomErrorMessage(){
    Boolean condition = false;
    assertTrue(condition, "Condition must be true");
}

The above test fails with the following message:-

Condition must be true ==> expected: <true> but was: <false>
Expected :true
Actual   :false

Custom Error Message Supplier

assertTrue(boolean condition, Supplier messageSupplier)
We can also pass the supplier method in the last parameter of the invoked assertion method. This is a good choice to create complex error messages.

@Test
public void givenMap_whenNotContainsKey_thenFailWithCustomErrorMessageSupplier() {
    Map<String, String> myMap = Map.of("myKey", "myValue");
    String KEY = "anotherKey";
    assertTrue(myMap.containsKey(KEY), () -> String.format("The map doesn't contain the key: %s", KEY));
}

The above test fails with the following message:-

The map doesn't contain the key: anotherKey ==> expected: <true> but was: <false>
Expected :true
Actual   :false

That’s all about assertions in Junit 5. Thanks for Reading!