In this tutorial, we will write our first test cases. We are going to write Calculator class which will try to test using JUnit.

Overview

Let’s first write the application code. In this case, I have a Calculator application which has Calculator class as shown below.

 1public class Calculator {
 2    public int add(int num1, int num2) {
 3        return num1 + num2;
 4    }
 5
 6    public int subtract(int num1, int num2) {
 7        return num1 - num2;
 8    }
 9
10    public int multiply(int num1, int num2) {
11        return num1 * num2;
12    }
13
14    public int divide(int num1, int num2) {
15        return num1 / num2;
16    }
17}

Creating your First Test

In this Java file select class name and right-click, select “Show Context Actions”.

Show Context Actions

Next, click “Create Test”.

Create Test

This will create CalculatorTest class in src/test/java directory under the same package as the original application class Calculator. Currently, it looks like this.

1class CalculatorTest {
2
3}

Running Your First JUnit Test

Now, we will test one method at a time from our Calculator class. In JUnit, every test method needs to have @Test annotation. So, we will write our method like below.

1import org.junit.jupiter.api.Test;
2
3class CalculatorTest {
4    @Test
5    void testAdd() {
6        System.out.println("Running testAdd");
7    }
8}

“Running your First JUnit Tests” “Run Test”

After adding this code, you will see two green buttons on the left side of class declaration and the method. The first one allows to run whole CalculatorTest class which at the moment includes only one test method as shown on line 5 above. The second one allows to run specific method in case we want to check only one method and nothing else. In this case, we have only added print message but still the test case passes which is wrong, but you can see that when we run the test case, it will print the output on the console like any other method in Java.

Let’s add some valid test cases. Modify this code file like below.

 1import org.junit.jupiter.api.Test;
 2
 3import static org.junit.jupiter.api.Assertions.assertEquals;
 4
 5class CalculatorTest {
 6    @Test
 7    void testAdd() {
 8        Calculator calculator = new Calculator();
 9        assertEquals(2, calculator.add(1, 1));
10    }
11}

In this method, we first create Calculator instance so that we can invoke the method add() on that instance. The next line has one assertion on line 9. We will learn more about assertions in upcoming lesson, but for now think of them as validating statement which validates that the output of add function is what we expected. In this case 1 + 1 = 2. The method signature is like assertEquals(Long expected, Long actual). In this case the second argument is the thing we want to validate and the first argument is what we expect the output to be.

Also important to note that the class and method does not have access modifier. This is because they should be run as individual files and we can ignore access modifiers for Test files.

When we run this code, we get green output.

“Successful Test Run”

Let’s make this test fail and see what it gives. Let’s change the expected value to be 100.

1class CalculatorTest {
2    @Test
3    void testAdd() {
4        Calculator calculator = new Calculator();
5        assertEquals(100, calculator.add(1, 1));
6    }
7}

In this case, the tests fail and we get red output.

“Failed Test Run”

Along with failed tests, the console gives the output why it failed.

org.opentest4j.AssertionFailedError: 
Expected :100
Actual   :2

This was the very basic test case. The passed tests do not mean our code is correct but it gives us a way to validate our methods. We may have to add than one assertions in single test method.

DisplayName annotation

Now, at the moment, it shows somewhat unusual description of methods. What if we had two different methods for testing add() functionality of the CalculatorTest. In this case, two methods will have different names but still it will be hard to read. Check below example with two methods.

 1import org.junit.jupiter.api.Test;
 2
 3import static org.junit.jupiter.api.Assertions.*;
 4
 5class CalculatorTest {
 6    @Test
 7    void testAdd() {
 8        Calculator calculator = new Calculator();
 9        assertEquals(2, calculator.add(1, 1));
10    }
11
12    @Test
13    void testAddNegativeNumbers() {
14        Calculator calculator = new Calculator();
15        assertEquals(-5, calculator.add(-2, -3));
16    }
17}

Again as previous method, we need to annotate the method with @Test which will mark the method as JUnit test method. When we run this method, it shows output like below.

Two methods under test

In this case, it’s unclear exactly what these methods are testing. So, some developers use long naming conventions. That convention may use camel-case notation or some other notation like below.

1void testPositiveAddition_WhenBothNumbersPositive_ShouldReturnCorrectResult() {
2
3}
4
5void testNegativeAddition_WhenBothNumbersNegative_ShouldReturnCorrectResult() {
6
7}

Although this gives slightly more idea on what method does, it is still hard to read. To solve this, we can use @DisplayName annotation on test classes and test methods. This gives us better idea on what each method does or which method failed.

 1import org.junit.jupiter.api.DisplayName;
 2import org.junit.jupiter.api.Test;
 3
 4import static org.junit.jupiter.api.Assertions.*;
 5
 6@DisplayName("Calculator Test")
 7class CalculatorTest {
 8    @Test
 9    @DisplayName("Test Positive Numbers Addition")
10    void testAdd() {
11        Calculator calculator = new Calculator();
12        assertEquals(2, calculator.add(1, 1));
13    }
14
15    @Test
16    @DisplayName("Test Negative Numbers Addition")
17    void testAddNegativeNumbers() {
18        Calculator calculator = new Calculator();
19        assertEquals(-5, calculator.add(-2, -3));
20    }
21}

In above code, we have added @DisplayName to the test class and both test methods. Now, if we execute the test class, we see output like this.

Test Run with DisplayName

This annotation makes it easier to read test results. This can be very useful when we have hundreds of test cases and we have to read which ones failed.