taylorialcom/ Tools

Testing with JUnit

Java Unit Tests

It is important to have assurances that the software we write works as expected. We can do this by running our program and verifying that it works as expected; however, every time we make a change to our code, it would be exhuasting to need to rerun our code and test every possible thing to make sure it all still works. Fortunately, we know how to write software, and we can write software to help us with this repetive task of verifying that our code still works.

Unit tests consist of code that tests small software units, e.g., a method. A popular framework for writing unit tests is the Jupiter testing framework provided in the JUnit platform. Here we will see how to make use of JUnit 5 to generate unit tests.

Most of these are pretty straight forward, but the let's take a moment to look at the last one. Executable is a functional interface with one method: void execute(). We can implement the interface with a lambda expresion:

Assertions.assertThrows(IndexOutOfBoundsException.class, () -> list.add(-1, null));

If the call to list.add(-1, null) doesn't throw an IndexOutOfBoundsException, the assertion fails.

Configuring IntelliJ

Since the JUnit platform is not part of the Java language, we need to configure IntelliJ to make use of it. IntelliJ makes this pretty easy, but be sure to select JUnit 5 rather than JUnit 4. To install and configure JUnit 5:

  1. Add the following import to your test class: import org.junit.jupiter.api.*;
  2. From the error context menu select Add 'JUnit 5.8.1' to classpath
    Figure [add]: Add JUnit 5
  3. Select OK on the pop-up window to download JUnit and install it in your project
    Figure [download]: Download JUnit 5

Sample Tests2

Consider the following test class that actually doesn't test anything but shows how the annotations control the execution of the tests.

import org.junit.jupiter.api.*;

class NothingTest {
    private static int count;

    @BeforeAll
    public static void initialize() {
        count = 0;
        System.out.println("Starting all tests with a count of " + count++);
    }

    @Test
    public void firstTest() {
        System.out.println("Running first test with count of " + count++);
    }

    @Test
    public void secondTest() {
        System.out.println("Running second test with count of " + count++);
    }

    @AfterAll
    public static void windDown() {
        System.out.println("All test finished with count of " + count++);
    }

}

If the test for the class are run, we would expect to output of either:

Starting all tests with a count of 0
Running first test with count of 1
Running second test with count of 2
All test finished with count of 3

or

Starting all tests with a count of 0
Running second test with count of 1
Running first test with count of 2
All test finished with count of 3

It could be either because the order in which tests are run is not defined.

If the following methods are added

    @BeforeEach
    public void setup() {
        System.out.println("Runs before each test with a count of " + count++);
    }

    @AfterEach
    public void teardown() {
        System.out.println("Runs after each test with a count of " + count++);
    }

then the output looks like this:

Starting all tests with a count of 0
Runs before each test with a count of 1
Running first test with count of 2
Runs after each test with a count of 3
Runs before each test with a count of 4
Running second test with count of 5
Runs after each test with a count of 6
All test finished with count of 7

Here you can see that the method annotated with @BeforeEach runs before each test method, and the method annoted with @AfterEach runs after each test.

1

Several overloaded methods for assertEquals() and assertNotEquals() are available for primitive types and the boxed primitive types. E.g., assertEquals(int, int), assertNotEquals(Integer, Integer) 2 that actually don't test anything