Introduction to JUnit

This lab is intended to present an introduction to the JUnit framework. By the end of this lab, you should have a basic understanding of what JUnit is used for, and be able to write some simple JUnit tests on your own.

Resources

1. What is JUnit?

JUnit is a unit testing framework for the Java programming language. Units are the smallest module of functionality in a computer program. These are usually in the form of a method. Therefore, JUnit is most commonly used to test the functionality of individual methods. Experience with JUnit has been important in the development of Test-Driven Development, which will be introduced in Lab 2.

2. JUnit 4 Syntax

This section will guide you through JUnit 4 syntax. A Java class holds source-code methods. Similarly, A JUnit test case holds test-code in unit tests. A JUnit test case is usually associated with a single Java class. It is generally good practice to have a JUnit test case for every Java class. Within that JUnit test case, one or more JUnit unit tests should be written for every source-code method.

2.1. Imports

In JUnit 4.4, three packages should be imported:

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;


The static Assert class contains all the JUnit asserts to use. The Before class is used for running a method to initialize variables before every unit test, and the Test class is for creating unit tests.

2.2. Test Case

The declaration of a JUnit test case is exactly the same as a normal Java class. The traditional naming convention for a JUnit test case is to append the word 'Test' to the name of the Java class being tested. For example, if we were testing the class Foo, then the corresponding JUnit test case would traditionally be named FooTest.

2.3. @Before

The @Before annotation is used for a method usually named setUp(). There is only one of these methods per JUnit test case. This method is run once before every unit test (see next section).

2.4. @Test

The @Test annotation signals that the following method is a JUnit unit test. Each @Test method should test the functional correctness of one method in the corresponding Java class. The traditional method naming convention is the word 'test' followed by the name of the method being tested. For example, if we were writing a unit test for a method named fooBar() then our unit test would be named testFooBar().

Let's now look at a concrete example.

3. Example: Unit Tests for a Java Class

We first need some Java classes to test. The following AddSubtract class has two methods; one to add (line 14) and another to subtract (line 21). Similarly, the DivideMultiply class has two methods which let you divide (line 21) or multiply (line 14) a given number with the class variable.


1  public class AddSubtract {
2     private int currentVal;
3  
4     /**
5      * Constructor to initialize our member variable.
6      */
7     public AddSubtract(
int a) {
8        currentVal = a;
9     }
10
11    /**
12     * Add the value of 'a' to our current value.
13     */
14    
public void add(int a) {
15       currentVal += a;
16    }
17
18    /**
19     * Subtract the value of 'a' from our current value.
20     */
21    
public void subtract(int a) {
22       currentVal -= a;
23    }
24
25    /**
26     * Get the current value.
27     */
28    
public int getCurrentVal() {
29       return currentVal;
30    }
31 }

--------------------------------------------------------------------------

1  public class DivideMultiply {
2     
private double currentVal;
3  
4     /**
5      * Constructor to initialize our member variable.
6      */
7     
public DivideMultiply(double a) {
8        currentVal = a;
9     }
10
11    /**
12     * Multiply 'a' with our current value.
13     */
14    
public void multiply(double a) {
15       currentVal *= a;
16    }
17
18    /**
19     * Divide 'a' from our current value.
20     */
21    
public void divide(double a) {
22       if (a == 0.0) {
23          throw new java.lang.ArithmeticException("Can't divide by zero!");
24       }
25
26       currentVal /= a;
27    }
28
29    /**
30     * Get the current value.
31     */
32    
public double getCurrentVal() {
33       return currentVal;
34    }
35 }

Let's now look at the corresponding JUnit test cases for the previous two Java classes.

1  import static org.junit.Assert.*;
2  import org.junit.Before;
3  import org.junit.Test;
4  
5  public
 class AddSubtractTest {
6     private AddSubtract as; // An instance of the class under test
7
8     /**
9      * This '@Before' method is ran before every '@Test' method
10     */
11    @Before
12    public void setUp() throws Exception {
13       as = new AddSubtract(4);
14    }
15
16    /**
17     * Test method for add()
18     */
19    @Test
20    public void testAdd() {
21       as.add(5);
22       assertEquals(9, as.getCurrentVal());
23       as.add(3);
24       assertEquals(12, as.getCurrentVal());
25    }
26
27    /**
28     * Test method for subtract()
29     */
30    @Test
31    public void testSubtract() {
32       as.subtract(5);
33       assertEquals(-1, as.getCurrentVal());
34       as.subtract(-3);
35       assertEquals(2, as.getCurrentVal());
36    }
37
38    /**
39     * Test method for getCurrentVal()
40     */
41    @Test
42    public void testGetCurrentVal() {
43       assertEquals(4, as.getCurrentVal());
44    }
45 }

--------------------------------------------------------------------------

1  import static org.junit.Assert.*;
2  import org.junit.Before;
3  import org.junit.Test;
4  
5  public
 class DivideMultiplyTest {
6     private DivideMultiply dm; // An instance of the class under test
7
8     /**
9      * This '@Before' method is ran before every '@Test' method
10     */
11    @Before
12    public void setUp() throws Exception {
13       dm = new DivideMultiply(3.0);
14    }
15
16    /**
17     * Test method for multiply()
18     */
19    
@Test
20    public void testMultiply() {
21       dm.multiply(2.0);
22       assertEquals(6.0, dm.getCurrentVal(), 0.0000001);
23    }
24
25    /**
26     * Test method for divide()
27     */
28    
@Test
29    public void testDivide() {
30       dm.divide(2.0);
31       assertEquals(1.5, dm.getCurrentVal(), 0.0000001);
32    }
33
34    /**
35     * Test method for the exception that divide() throws
36     */
37    
@Test(expected=java.lang.ArithmeticException.class)
38    public void testDivideByZero() {
39       dm.divide(0.0);
40    }
41
42    /**
43     * Test method for getCurrentVal()
44     */
45    @Test
46    public void testGetCurrentVal() {
47       assertEquals(3.0, as.getCurrentVal(), 0.0000001);
48    }
49 }

Notice our @Before method in both our test cases initializes the AddSubtract and DivideMultiply classes which we want to test. Since the setUp() method (line 12 in both test-cases) runs before every unit test, it reinitializes our object for every test. So how do we write a test? We use assert statements, some of which are overviewed in the following section.

3.1. Assert statements

JUnit provides a number of assert statements (full list found here under the Assert class). In both our JUnit test cases (AddSubtractTest and DivideMultiplyTest), we use the assertEquals() method. In our testAdd(), line 20, and testSubtract(), line 21, methods within the AddSubtractTest class, the assertEquals() method takes two parameters. The first parameter is the value we expect the method will return. The second parameter is what the actual value is. Let's look at the following assert statement:
      assertEquals(6, 3*2);
In this assert, we expect the result of 3*2 to be 6, so we put that as the first parameter. The second parameter is then the result of what 3*2 is.

In our second JUnit class DivideMultiplyTest, testMultiply(), line 20, and testDivide(), line 29, both use a three parameter assertEquals() method. The first two parameters are the same as the two parameter version, the expected result and actual result, respectively. The third parameter is a delta value for comparing doubles and floats with fractional values.

3.2. Exception handling

If a method is supposed to throw an exception due to bad data, we can test for that very easily. In our divide() method in the DivideMultiply Java class, it throws an ArithmeticException if the denominator is zero, since we can't divide by zero. We can write a test where we expect an exception to be thrown. To do this, we put the class file of the expected exception in parentheses right after the Test annotation. The format is to have the word expected followed by an equals (=) then the Exception.class file all in parentheses after the @Test annotation (see line 37 of DivideMultiplyTest).

4. Exercise: A Banking account

For this exercise, you will create two files: Account.java and AccountTest.java. You will have to develop the source-code and test-code on your own computer, and Web-CAT will be used to submit the exercise for grading. If you use Eclipse, JUnit comes already installed - ask your instructor if you have trouble setting it up. If you use JCreator or JGrasp, instructors for integrating are found here for JCreator and here for JGrasp. If you use any other IDE, contact your instructor to figure out how to set up JUnit with your IDE. The objective of this assignment is to write a class to demonstrate bank account functionality, but then create a corresponding JUnit test case to ensure its correctness. The Account.java class has to have the following methods and member variables.

4.1. Submission Process

To hand in your files, click on the Submit tab in Web-CAT. Follow the instructions to upload your files. Remember that you will need to create a *.zip file with your two Java files (Account.java and AccountTest.java) in it. Web-CAT will automatically extract your two files from the zip archive.

Chetan Desai - cdesai@calpoly.edu