JUnit v5 (Jupiter) Basics

Introduction

JUnit is an open-source testing framework. It provides a way to write, organize, and run repeatable test. This page provides a brief introduction to v5. Information is also available about version 4 .

Creating Test Classes

For each class you want to test, you should create a separate file. While the file can be named anything, it is common to use a scheme like ClassNameTest.java where ClassName denotes the name of the class being tested. So, for example, if you want to test the class named Atom you would create a file named AtomTest.java.

The ClassNameTest.java file must begin with the following lines:

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

and must include a class declaration of the following form:

public class ClassNameTest

So, continuing with the example above, AtomTest.java would initially contain the following:

import static org.junit.jupiter.api.Assertions.*;
 
import org.junit.jupiter.api.Test;
 
class AtomTest
{
 
}

(Note: The import static allows you to refer to static members in the org.junit.jupiter.api.Assertions class without having to include the class name.)

The file ClassNameTest.java contains methods that can be used to test the class defined in ClassName.java. Each such method is preceded by an @Test annotation. (Note: An annotation provides information about a program but is not part of the program. Annotations have no effect on the operation of the program. Instead, they are used to provide information to tools that might use the program as input.)

So, still continuing with the example above, if the Atom class contains a public int getAtomicNumber() method, then AtomTest.java might include the following method:

    /**
     * Unit tests for the getAtomicNumber() method
     */
    @Test
    public void getAtomicNumber()
    {
    }

Note that the name of this method is completely arbitrary, but, as always, descriptive names are always a good idea. Some people simply use the name of the method being tested (as above), while others add a prefix or suffix that includes some form of the word “test” (e.g., testGetAtomicNumber(), getAtomicNumber_Test(), etc…).

The body of the methods in the ClassNameTest.java must contain the test cases for the corresponding methods in the ClassName class. These test cases often involve some “setup” code and a call to the Assertions.assertEquals() method. So, for example, the getAtomicNumber method in the Atom class is supposed to return the atomic number of the calling Atom object. To partially test this method one might implement the testGetAtomicNumber() method in the AtomTest class as follows:

    /**
     * Unit tests for the getAtomicNumber() method
     */
    @Test
    public void getAtomicNumber()
    {
        Atom    o;
 
        o = new Atom("O", 8, 16);
 
        assertEquals(8, o.getAtomicNumber(), "Oxygen");
	// Note:
        // This is a call to Assertions.assertEquals() but the class name
	// isn't needed because of the static import.
    }

In the “setup” portion, this method creates an Atom objects (for oxygen). It then calls the Assertions.assertEquals() method to tell JUnit to do some testing. This particular version of the Assertions.assertEquals() method is passed three parameters and has the following syntax:

Assertions.assertEquals(ExpectedValue, ActualValue, Description);

The Description is a human-readable String that provides information that enables the tester to understand the test in the event that the code fails the test. (Note that the tester will also be provided with the name of the test method so it isn't necessary to include information about the method being tested in the description if the test method is named well.) The ExpectedValue contains the correct value (i.e., the value that the tester expects if the method named MethodName in ClassName is working correctly. The ActualValue contains the value that was actually generated by the method named MethodName in ClassName.

In the example above, the getAtomicNumber() method in the Atom class is being tested. A call to o.getAtomicNumber() should return 8.

As another example, suppose the Atom class contains a public boolean equals(Atom other) method, then AtomTest.java might implement the following method:

    /**
     * Unit tests for the equals(Atom) method
     */
    @Test
    public void equals_Test()
    {
    }

Note that, even though the equals() method in the Atom class is passed an Atom object and returns a boolean, the equals_Test() method in AtomTest.java has no parameters and does not return anything.

The equals() method in the Atom class is supposed to compare the calling Atom object with the given Atom object and return true if the two have the same attributes and false otherwise. To partially test the equals() method, one might implement the testEquals() method in the AtomTest class as follows:

    /**
     * Unit tests for the equals(Atom) method
     */
    @Test
    public void equals_Test()
    {
      Atom    h, hh, o;
 
      h  = new Atom("H", 1, 1);
      hh = new Atom("H", 1, 1);
      o  = new Atom("O", 8, 16);
 
      assertEquals(true, h.equals(hh), "Two H atoms");
 
      assertEquals(false, h.equals(o), "H and O");
      assertEquals(false, o.equals(h), "O and H");
    }

The equals(Atom) method in the Atom class is being tested. A call to h.equals(o) should return false as should a call to h.equals(o).

More About the Assertions.assertEquals() Method

The Assertions.assertEquals() method can be used to compare the expected and actual values of a wide variety of different types. In the examples above it is used to compare int values and boolean values. It can also be used to compare String objects and other objects, but you have to be careful when doing so. When assertEquals() is used to compare primitive types, it uses the == operator. When assertEquals() is used to compare class types, it uses the .equals() method (with special handling for null references).

When using Assertions.assertEquals() to compare floating point numbers (e.g., double values), one must remember that the == operator must be used with care because of the less-than-perfect precision of operations on double values. In JUnit, the implication of this is that one should check to see if double values are within a tolerance value of each other. Hence, when comparing double values one should use the following:

Assertions.assertEquals(ExpectedValue, ActualValue, tolerance, Description);

Testing Methods that Throw Exceptions

There are several ways to test for thrown exceptions in JUnit. The most common is to use the assertThrows() method, but this approach depends on an advanced feature of Java (and many languages) called "lambda expressions", so we will demonstrate a less commonly used, but simpler, approach first.

Testing for exceptions

One can invoke the methods that is supposed to throw an expression in a try-catch block, and use the fail() method. For example:

  /**
   * Test that the constructor validates properly.
   */
  @Test
  public void constructor_IllegalArguments()
  {
    try
    {
      new Atom("O", -8, -16);
 
      // Shouldn’t get here
      fail("Constructor should have thrown an IllegalArgumentException");
    }
    catch (IllegalArgumentException iae)
    {
      // The exception was thrown as expected
    }   
  }

Testing for exceptions canonically (with lambda expressions)

Suppose the constructor of the Atom() class is required to throw an IllegalArgumentException when the numeric parameters are negative. One might test this is follows.

  /**
   * Test that the constructor validates properly.
   */
  @Test
  public void constructor_IllegalArguments()
  {
    assertThrows(IllegalArgumentException.class, () -> {new Atom("O", -8, -16);});
  }

This approach uses a Lambda expression, a representation of a class with a single-method.

Other Useful Methods in the Assertions Class

The Assertions class contains other useful methods including:

assertArrayEquals() for arrays of different types
assertFalse() and assertTrue() for boolean values
assertNull() for references
assertSame() for references

Using these methods, it would be better to write the test of the equals() method above as follows.

    /**
     * Unit tests for the equals(Atom) method
     */
    @Test
    public void equals_Test()
    {
      Atom    h, hh, o;
 
      h  = new Atom("H", 1, 1);
      hh = new Atom("H", 1, 1);
      o  = new Atom("O", 8, 16);
 
      assertTrue(h.equals(hh), "Two H atoms");
 
      assertFalse(h.equals(o), "H and O");
      assertFalse(o.equals(h), "O and H");
    }

The @BeforeEach and @BeforeAll Annotations

Sometimes several tests need to use the same objects. This can be accomplished using the @BeforeAll or BeforeEach annotations, which instruct JUnit to run the methods with this annotation before the first test or before each test, respectively..

The @BeforeEach and @BeforeAll annotations are very useful when an individual test changes the state of an object since, in general, one wants test to be independent. (Note that, in general, you should not assume that the tests themselves will be run in a particular order.)

To use these annotation you must import org.junit.jupiter.api.BeforeEach and/or org.junit.jupiter.api.BeforeAll.

Downloading JUnit

Information about how to download and install JUnit is available from junit.org . The .jar file will be named something like junit-platform-console-standalone.jar (which is referred to below as junit.jar for simplicity).

Compiling and Running JUnit Tests from a Command Shell/Terminal Window

Using JUnit with the BASH Shell

Configuration

The Java compiler needs to be able to “find” the JUnit classes and the Java intepreter needs to be able to find both the JUnit classes and the Hamcrest classes. For this discussion, we will assume that the JUnit classes are in junit.jar and the Hamcrest classes are in hamcrest-core.jar (though the actual names will vary). You can either identify the location of these files each time you use the compiler/interpreter or you can set an operating system environment variable named CLASSPATH.

Using the CLASSPATH Environment Variable

You can set the CLASSPATH as follows:

export CLASSPATH=.://directory//junit.jar:$CLASSPATH

where directory denotes the name of the directory/folder that contains the file junit.jar.

For example, assuming that the .jar file is in the current working directory, you can set the CLASSPATH as follows:

export CLASSPATH=.:junit.jar:$CLASSPATH

After you have set the CLASSPATH you can compile your classes (including the test classes) and the usual way, and run a JUnit test as follows:

java org.junit.platform.console.ConsoleLauncher --select-class  //ClassName//Test

where ClassName is the name of the class being tested. For example, to run AtomTest:

java org.junit.platform.console.ConsoleLauncher --select-class  AtomTest
Not Using the CLASSPATH Environment Variable

If you don't use set the CLASSPATH you must tell Java where to find JUnit every time you compile and/or run a test.

To compile:

javac -cp .:junit.jar ClassNameTest.java

where Name represents tha name of the class being tested (and can contain wildcards like *).

To run:

java -cp .:junit.jar org.junit.platform.console.ConsoleLauncher --select-class ClassNameTest

where Name represents the name of the class being tested.

For example, to compile and run AtomTest:

javac -cp .:junit.jar AtomTest.java
java -cp .:junit.jar org.junit.platform.console.ConsoleLauncher --select-class   AtomTest
Test Output

If all of the tests are successful, an “OK” message will be printed. If not, a long error message will be printed. Buried in the message will be information about where the errors occurred and what the errors were. To find them, look for the descriptions that you included in your calls to assertEquals().

Using JUnit with the MS-Windows CMD Shell

Configuration

The Java compiler needs to be able to “find” the JUnit classes and the Java intepreter needs to be able to find the JUnit classes. For this discussion, we will assume that the JUnit classes are in junit.jar (though the actual names will vary). You can either identify the location of these files each time you use the compiler/interpreter or you can set an operating system environment variable named CLASSPATH.

Using the CLASSPATH Environment Variable

You can set the CLASSPATH as follows:

set CLASSPATH=.;directoryjunit.jar;%CLASSPATH%

where directory denotes the name of the directory/folder that contains the file junit.jar.

For example, assuming that the .jar file is in the current working directory, you can set the CLASSPATH as follows:

set CLASSPATH=.;junit.jar;%CLASSPATH%

After you have set the CLASSPATH you can compile your classes (including the test classes) and the usual way, and run a JUnit test as follows:

java org.junit.platform.console.ConsoleLauncher --select-class ClassNameTest

where ClassName is the name of the class being tested. For example, to run AtomTest:

java  org.junit.platform.console.ConsoleLauncher --select-class   AtomTest
Not Using the CLASSPATH Environment Variable

If you don't use set the CLASSPATH you must tell Java where to find JUnit every time you compile and/or run a test.

To compile:

javac -cp .:junit.jar ClassNameTest.java

where Name represents tha name of the class being tested (and can contain wildcards like *).

To run:

java -cp .:junit.jar org.junit.platform.console.ConsoleLauncher --select-class ClassNameTest

where Name represents the name of the class being tested.

For example, to compile and run AtomTest:

javac -cp .:junit.jar AtomTest.java
java -cp .:junit.jar org.junit.platform.console.ConsoleLauncher --select-class   AtomTest
Test Output

If all of the tests are successful, an “OK” message will be printed. If not, a long error message will be printed. Buried in the message will be information about where the errors occurred and what the errors were. To find them, look for the descriptions that you included in your calls to assertEquals().

Integrating JUnit into an IDE

For More Information

More information is available at the JUnit FAQ and the JUnit wiki .