Java. JUnit testing. Example

JUnit testing. Examples


Contents


Search other resources:

1. The concept of unit testing in Java

In Java programming systems, including Java Eclipse, testing tools have been introduced that provide for the creation and use of automated tests for the developed programs. Correctly constructed testing allows you to reduce the number of errors in the program to zero.

The TDD (Test Driven Development) methodology is popular in the development of modern software systems. The main idea of using TDD is that first tests are developed for the desired behavior of a program (system), and then a working program code is written, which is checked by the already created tests using refactoring tools. As a result, pre-prepared tests allow you to avoid putting into operation a program containing errors.

For testing, Java Eclipse uses what is called JUnit testing, which is the de facto standard for testing Java applications. JUnit is a common framework for writing tests as Java classes. These classes can be run as a unit using the test runner.

JUnit has many extensions that are implemented in all modern Java development environments. Today JUnit is used in such directions as:

  • unit testing;
  • website testing;
  • automatic substitution of interfaces for testing;
  • testing parallel execution of applications;
  • performance testing.

JUnit testing is used by many quality assurance teams on purpose-built websites that automate the application of end-to-end functional tests. In Java Eclipse, using the JUnit engine does not require a running application server or a live database.

 

2. The notion of Test Case. A class containing test cases (methods). Test run

When using the JUnit engine, two main units (classes) are considered:

  • the class whose methods need to be tested;
  • a class that represents a JUnit test. This class is built according to special rules.

This means that a JUnit test is a specially designed class containing methods that test the code of other classes. In Java Eclipse, this class is created using the JUnit Test Case command (see examples below).

The class contains a number of methods. Each of the methods of the testing class is treated as a separate test caseTest Case.

Test cases (methods) of a JUnit class can be declared using the following annotations (Java version Eclipse 2018-09 is considered):

  • @BeforeAll – static method (test case), declared with this instruction, is called at the beginning of testing;
  • @AfterAll – the static method declared with this instruction is called at the end of testing. Here you can set, for example, the release of resources used during testing;
  • @Test – the method that is declared with this instruction is a test case. It contains directly the testing code. This method uses the class org.junit.jupiter.api.Assertions methods. This method uses comparison methods that start with the assert*() prefix. For example, the overloaded assertEquals() method is used to compare two values of any primitive type. One JUnit test class can have an arbitrary number of test methods (examples), which are declared with the @Test annotation;
  • @BeforeEach – called before calling each test case (method), which is declared with the @Test annotation;
  • @AfterEach – called after the completion of each test case (method) that is declared with the @Test annotation.

An arbitrary number of methods are allowed with all of the above annotations.

In the most general case, an example of a class generated by the Java Eclipse system might look like this

class TestClass {

  @BeforeAll
  static void setUpBeforeClass() throws Exception {
  }

  @AfterAll
  static void tearDownAfterClass() throws Exception {
  }

  @BeforeEach
  void setUp() throws Exception {
  }

  @AfterEach
  void tearDown() throws Exception {
  }

  @Test
  void testMethod() {
    fail("Not yet implemented");
  }
}

In the testMethod() method, you need to insert your own code for the methods of another class (see examples below). After that, you can run the test using a dedicated Java Eclipse command.

 

3. An example demonstrating the use of JUnit to validate a quadratic equation solution. Step-by-step instruction
3.1. Task

Using JUnit technology, develop a Unit test to test the operation of the SquareEquation class, which contains tools for solving a quadratic equation. The test should be located in the SquareEquationTest class and contain appropriate methods to validate the resulting solution.

Quadratic roots are returned as an instance of the Roots class. Class SquareEquationTest comprises appropriate means solve the quadratic equation.

 

3.2. Solution
3.2.1. Creating the SquareEquation and Roots classes

Before creating the classes, the project is created in the standard Java Eclipse way. Two classes named SquareEquation and Roots are added to the project and the code for these classes is generated. The SquareEquation.java file is created accordingly. After the completed actions, the Java Eclipse window looks as shown in Figure 1.

Java. JUnit-testing. Classes Roots and SquareEquation

Figure 1. Java Eclipse window. Classes Roots and SquareEquation

Classes have the following purpose:

  • Roots – used to save the roots of a quadratic equation (x1, x2) if the equation has these roots;
  • SquareEquation – implements methods for solving a quadratic equation.

The SquareEquation class has the following components:

  • internal variables a, b, c which are the coefficients of the quadratic equation;
  • constructor, initializes the values of the internal fields a, b, c;
  • the Solution() method, which returns an instance of the Roots class if the equation has roots. If the equation has no roots, the ArithmeticException is thrown.

The full text of the Roots and SquareEquation classes looks like this

// The roots of the equation
class Roots {
  public double x1;
  public double x2;
}

// Class that solves the quadratic equation - this class will be tested by another class.
// a*x^2 + b*x + c = 0
public class SquareEquation {

  // The coefficients of the equation
  private double a, b, c;

  // Constructor
  public SquareEquation(double a, double b, double c) {
    this.a = a; this.b = b; this.c = c;
  }

  // The method of solving the quadratic equation
  public Roots Solution() {
    // Discriminant
    double d = b*b - 4*a*c;

    // Checking if the equation has roots
    if (d<0)
      throw new ArithmeticException("Solution has no roots.");

    // Calculate the result
    Roots result = new Roots();
    result.x1 = (-b - Math.sqrt(d)) / (2*a);
    result.x2 = (-b + Math.sqrt(d)) / (2*a);

    // Return the result
    return result;
  }
}

After creating the classes, you can proceed to creating a Unit-test.

 

3.2.2. Creating the class needed to implement JUnit testing

To create a Unit test in Java Eclipse, you need to select a sequence of commands

File->New->JUnit Test Case

as shown in Figure 2.

Java Eclipse. Command to create Unit-test in JavaFigure 2. Command to create Unit-test in Java

As a result, the “New JUnit Test Case” window will open (Figure 3) in which you need to specify the necessary information about the test.

Java Eclipse. New JUnit Test Case windowFigure 3. New test creation window. Specifying test parameters

When creating a window, the system will automatically fill in some fields. The window displays the following information.

  1. The Source Folder field specifies the name of the folder with the source files of the project. The system automatically “pulls up” the name of the folder with the current project.
  2. In the Name field, specify the name of the class in which the test methods will be placed (with the annotations @BeforeAll, @Test, etc.). In our case, SquareEquationTest is offered (the Test suffix is added to the SquareEquation class name). The proposed name can be left unchanged.
  3. In the field “Which method stubs would you like to create?” the options are used to specify the methods to be generated in the class. According to the selected option, the following methods are generated:
    • setUpBeforeClass() – the name of the @BeforeAll annotation method (see item 2);
    • setUp() – the name of the @BeforeEach annotation method;
    • tearDownAfterClass() – the name of the @AfterAll annotation method;
    • tearDown() – the name of the @AfterEach annotation method.

For our test, it is enough to activate only one setUp() field. This will add an @BeforeEach annotation method that will be called before the test. This method will instantiate the SquareEquation class (see codes below).

  1. In the “Class under test:” field, specify the name of the SquareEquation class whose methods you want to test.
  2. Additionally, other options are set:
    • selection of the testing model “New JUnit Jupiter test”;
    • the ability to add comments;
    • other.

In our case, you can leave everything unchanged.

After selecting the “Next >” button, the following window “New JUnit Test Case” will open in which you need to specify a list of methods that will be tested. The window view is shown in Figure 4. Selected methods will be declared with @Test annotation. These are directly test cases. All other options in the window can be left as default and go further with the Finish button.

Java Eclipse. New JUnit Test Case window

Figure 4. The choice of methods to be tested. The Solution() method is selected

 

3.2.3. Program code of SquareEquationTest class

After creating the JUnit test using the Java window interface, Eclipse generates the code for the SquareEquationTest class

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

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

class SquareEquationTest {

  @BeforeEach
  void setUp() throws Exception {
  }

  @Test
  void testSolution() {
    fail("Not yet implemented");
  }
}

In this code, two methods are generated:

  • the setUp() method with the @BeforeEach annotation. This method will be called first when testing;
  • method testSolution() with the annotation @Test. In this method, the test of the Solution() function of the SquareEquation class will be placed directly.

 

3.2.4. Modification of the SquareEquationTest class. Test programming

To test the operation of the SquareEquation class, you need to create an instance of this class. Therefore, in the text of the SquareEquationTest class, a reference to the SquareEquation class is additionally introduced.

class SquareEquationTest {

  private SquareEquation se;

  ...
}

Then, in the setUp() method, an instance of the SquareEquation class is instantiated

@BeforeEach
void setUp() throws Exception {
  // This method is called first.
  // Create an instance of the SquareEquation class.
  se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0
}

After that, in the TestSolution() method, the code for checking the correctness of the obtained solution for the given coefficients is entered

a = 2
b = 1
c = -3

in the se instance

@Test
void testSolution() {
  // Declare an instance of the Roots class
  Roots rt = se.Solution();

  // Solution check x1
  assertEquals(rt.x1, -1.5);

  // Solution check x2
  assertEquals(rt.x2, 1.0);
}

The solution is verified using the overloaded assertEquals() method of the org.junit.jupiter.api.Assertions class. The solution in the variables rt.x1 and rt.x2 is compared with the previously calculated (manually) solution of the current variant (a = 2, b = 1, c = -3) of the quadratic equation. If there is a match, then the decision is correct. The system needs to tell the system that the decision is correct after starting the test (next point).

In general, the text of the entire testing unit is as follows

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

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

class SquareEquationTest {

  // A reference to the SquareEquation class
  private SquareEquation se;

  @BeforeEach
  void setUp() throws Exception {
    // This method is called first.
    // Create an instance of the SquareEquation class
    se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0
  }

  @Test
  void testSolution() {
    // Declare an instance of the Roots class
    Roots rt = se.Solution();

    // Solution check x1
    assertEquals(rt.x1, -1.5);

    // Solution check x2
    assertEquals(rt.x2, 1.0);
  }
}

 

3.3. Running the test using Java Eclipse. Viewing the test result

After the code for the SquareEquationTest class is generated, the developed test can be run for execution. This is done by the commands

Run -> Run As -> JUnit Test

or by calling the sequence of key combinations Alt + Shift + X, T as shown in Figure 5.

Java Eclipse. Running JUnit-test

Figure 5. Running a JUnit Test

There is another way using the quick menu command (Figure 6).

Java Eclipse. Command to run JUnit test

Figure 6. Command to run JUnit test

After starting Java Eclipse, the test result will be displayed in the JUnit window on the left side (Figure 7).

Java Eclipse. JUnit-test. Testing result

Figure 7. Testing result

As you can see from the result, the confirmation of the correct decision was received, that is, the test was passed. This means that the Solution() method of the SquareEquation class gives the correct result for the case when a = 2, b = 1, c = -3. If in the TestSolution() method, when calling assertEquals(), you specifically specify an incorrect answer, for example

assertEquals(rt.x2, 777.777);

then after re-running the test, an error will be displayed in the JUnit window, as you can see in Figure 8. Although this is not a program error, but a deliberate error in the test.

Java Eclipse. JUnit-testing. Demonstration of test failureFigure 8. Demonstration of test failure

 

4. An example showing the sequence of method calls in a JUnit test

Let a Java Eclipse system create a class named TestClass to test some other class. In this example, the essence of the class under test is irrelevant.

The program code of the class is as follows

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

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class TestClass {

  @BeforeAll
  static void setUpBeforeClass() throws Exception {
    System.out.println("BeforeAll");
  }

  @BeforeAll
  static void setUpBeforeClass2() throws Exception {
    System.out.println("BeforeAll2");
  }

  @AfterAll
  static void tearDownAfterClass() throws Exception {
    System.out.println("AfterAll");
  }

  @BeforeEach
  void setUp() throws Exception {
    System.out.println("BeforeEach");
  }

  @BeforeEach
  void setUp2() throws Exception {
    System.out.println("BeforeEach2");
  }

  @AfterEach
  void tearDown() throws Exception {
    System.out.println("AfterEach");
  }

  @Test
  void testSolution() {
    System.out.println("Test");
  }

  @Test
  void testSolution2() {
    System.out.println("Test2");
  }
}

After running the test for execution, the program will give the following result

BeforeAll
BeforeAll2
BeforeEach2
BeforeEach
Test
AfterEach
BeforeEach2
BeforeEach
Test2
AfterEach
AfterAll

After analyzing this result, you can conclude that the methods in the test class are called in the order defined by their annotations. The @BeforeAll annotation methods are called first.

The following are test cases declared with the @Test annotation. All methods with @BeforeEach annotation are called before each test case. After each test case, all @AfterEach annotated methods are called.

All methods with @AfterAll annotation are called last.

 


Related topics