Java JUnit Tutorial - Budapest University of Technology ... · BME Department of Control...
Transcript of Java JUnit Tutorial - Budapest University of Technology ... · BME Department of Control...
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
1
Java JUnit Tutorial
Author: Péter Budai, BME IIT, 2011.
The description of the annotations of JUnit can be found here:
http://junit.sourceforge.net/javadoc/
The most frequently asked questions about JUnit are answered here (especially sections 4, 5
and 7):
http://junit.sourceforge.net/doc/faq/faq.htm
The following JUnit Tutorial shows and example for parameterized testing:
http://www.mkyong.com/tutorials/junit-tutorials/
1 Installing the JUnit plugin
Eclipse has built in support for JUnit, but code coverage testing requires the EclEmma plugin. The
eclipse.zip archive found on the website of the subject already contains this plugin.
In case we don’t run Eclipse under Windows we have to install this plugin manually. To do this, select
the menu item Help/Install New Software... and specify the following:
After installing the plugin, restart Eclipse.
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
2
Important note: the EclEmma plugin works only with JRE/JDK 6, the new JDK 7 is not yet
supported! If JDK 7 is installed on the computer, make sure to set the generation of JDK 6
compatible class files in the project properties. (Right click on the project, select Properties, and in
the opening window under Java Compiler set the JDK Compliance.)
2 Creating a simple calculator class
Create a simple calculator class in Java which can multiply and divide double numbers. The most
simple features of JUnit will be demonstrated on this class.
Create a new Java project named JUnitTest:
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
3
Create a new class named Calculator under the package junittest:
Finally realize the functions inside the Calculator class. Create a multiply and a divide method. Make
sure to avoid division by zero. If the divisor is zero, throw an IllegalArgumentException:
package junittest;
public class Calculator {
public double multiply(double a, double b) {
return a * b;
}
public double divide(double a, double b)
throws IllegalArgumentException {
if(b == 0) {
throw new IllegalArgumentException();
}
return a / b;
}
}
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
4
3 Testing the Calculator class with JUnit 4
JUnit is an open source Java library which has become the most popular too for unit testing Java
programs. The JUnit framework contains classes which make testing units of the source code easier
and repeatable.
The JUnit framework is supported by most development environments, including Eclipse. In order to
be able to use this framework, its library must be added to the project. Right click on the project and
select the Properties menu item. In the opening window navigate to Java Build Path, Libraries and
click Add Library... Select JUnit and make sure to select version 4! The following window may help
you to find this option:
After selecting the right version, the project should look like as follows:
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
5
The test classes should be handled separately from the application logic since they should not be
added to the resulting .jar file. Therefore we should create a new source directory inside our Eclipse
project for the test classes. Right click on the project and select New > Source Folder. The directory
name should be test as shown in the following picture:
The test classes can also be organized into packages just like our normal classes. The convention is to
place the unit test classes in the same packages as the tested classes. Such way the test classes can
access package visible members of the tested classes.
Create a new JUnit Test Case under the test folder. Its name should be CalculatorTest under the
package junittest:
If there is no JUnit Test Case item, a simple Java class in the same package will suffice.
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
6
The JUnit test cases are represented by Java methods annotated by @Test and its companions
grouped into classes. These test cases will be executed by the framework and it will also summarize
the results. A test class can contain multiple test methods. Inside the test methods the static
assertXXX() functions of the class org.junit.Assert can be used to compare the real and the expected
results. These functions will automatically characterize the test cases as failed if the real and the
expected results differ. JUnit also makes it possible to specify that the expected behavior is an
exception. The exact way to do this can be read in the FAQ.
Let’s prepare out test cases. In this example we will only test a single pair of numbers, however, in a
real life scenario multiple pairs of numbers may be better. We will also check whether our class will
throw the expected exception in case the divisor is zero. The following is our test class:
package junittest;
import org.junit.Assert;
import org.junit.Test;
public class CalculatorTest {
@Test
public void testMultiply() {
Calculator calc = new Calculator();
double result = calc.multiply(5.0, 8.0);
Assert.assertEquals(40.0, result, 0);
}
@Test
public void testDivide() throws Exception {
Calculator calc = new Calculator();
double result = calc.divide(20.0, 4.0);
Assert.assertEquals(5.0, result, 0);
}
@Test(expected=IllegalArgumentException.class)
public void testDivideByZero() throws Exception {
Calculator calc = new Calculator();
calc.divide(10.0, 0.0);
}
}
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
7
When our test class is ready, select it in the Package Explorer, right click on it (or on the name of the
project) and choose the Run As/JUnit Test menu item. Eclipse will execute the test cases and will
show the results in the following window:
4 Using test fixtures
The JUnit frameworks runs the test methods independently from each other. The behavior of one
test method does not affect the behavior of another one. This is achieved by creating a new instance
of the test class for each test method call.
Testing more complex component may require setting up an object with a specific initial state. These
setup steps logically do not belong to the test. In addition, if multiple test methods use the same
initial state, these steps would have to be repeated for each method. Therefore, JUnit provides
means to create test initialization (annotated by @Before) and test finalization (annotated by
@After) methods. These initialization and finalization methods will be called before and after each
test method, respectively. This way a test environment called test fixture can be provided for the
tests.
Modify our test class so that the calculator object needs not to be created manually at the beginning
of each test. Place the instantiation into an initialization method. There is not much gain by this in
our example, but it is sufficient for demonstration purposes.
package junittest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
Calculator calc;
@Before
public void setUp() {
calc = new Calculator();
}
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
8
@Test
public void testMultiply() {
double result = calc.multiply(5.0, 8.0);
Assert.assertEquals(40.0, result, 0);
}
@Test
public void testDivide() throws Exception {
double result = calc.divide(20.0, 4.0);
Assert.assertEquals(5.0, result, 0);
}
@Test(expected=IllegalArgumentException.class)
public void testDivideByZero() throws Exception {
calc.divide(10.0, 0.0);
}
}
Note that the calculator object must be visible for all test methods, therefore, it has to be stored in
an attribute. If we have done everything correctly, all the tests must pass.
5 Code coverage with the EclEmma plugin
Emma is an open source Java library that can provide information about the execution of a Java
program: it can mark which statements have been executed during the run of the program or even
during JUnit tests. There is also an Eclipse plugin called EclEmma that integrates this tool with Eclipse
even showing which parts of the code have been executed.
Using the EclEmma plugin is really easy. Right click on the name of the project in the Package
Explorer and select Coverage As > JUnit Test. After running the tests a Coverage window will be
shown where the statistics can be viewed by files. Double clicking on a file name the source code will
be opened and the source lines will be colored if they have been executed, e.g.:
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
9
6 Parameterized testing with JUnit
When testing different algorithms it is usually desirable to test specific operations for different
inputs to make sure the program works even for extreme values. In this case we would have to write
a different test method for each input combination which can be very tedious for large parameter
domains. Luckily, JUnit offers a solution for this through parameterized tests.
A parameterized test must have a static method annotated by the @Parameters annotation and this
static method must return with the input combinations. The test methods in the test class will be
executed for each input combination. The test object will receive the input combination in its
constructor. All of this is illustrated in JUnit Tutorial (see above).
The static method returns with a collection of Object arrays (Collection<Object[]> or List<Object[]>,
etc.). The number of elements in the collection determines how many times the tests should be
executed. An Object array contains a single input combination. It can even contain the expected
results.
The number of items (.length) in an Object[] must be equal to the number of parameters of the
constructor of the test class, since these items will be passed to the constructor when the JUnit
framework instantiates the class.
For example, if the constructor of the test class has three parameters, and there are four input
combinations, then the static method must return with a collection of four Object arrays each with a
length of three.
In practice we usually store the input parameters of the constructor in attributes in the test class so
that we can access them later in the test methods.
Parameterized tests are special, since they cannot be executed by the default JUnit engine.
Therefore our test class must be annotated by the @RunWith annotation, in which the
parameterized execution engineorg.junit.runners.Parameterized has to be specified.
Now modify our test class so that it performs the multiplication and division test for five different
operand combination:
package junittest;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
10
@RunWith(Parameterized.class)
public class CalculatorTest {
double a;
double b;
Calculator calc;
public CalculatorTest(double a, double b) {
this.a = a;
this.b = b;
}
@Before
public void setUp() {
calc = new Calculator();
}
@Test
public void testMultiply() {
double result = calc.multiply(a, b);
Assert.assertEquals(a * b, result, 0);
}
@Test
public void testDivide() throws Exception {
double result = calc.divide(a, b);
Assert.assertEquals(a / b, result, 0);
}
@Test(expected=IllegalArgumentException.class)
public void testDivideByZero() throws Exception {
calc.divide(a, 0.0);
}
@Parameters
public static List<Object[]> parameters() {
List<Object[]> params = new ArrayList<Object[]>();
params.add(new Object[] {0.0, 0.0});
params.add(new Object[] {10.0, 0.0});
params.add(new Object[] {10.0, 3.0});
params.add(new Object[] {20.0, 4.0});
params.add(new Object[] {40.0, 5.0});
return params;
}
}
BME Department of Control Engineering and Information Technology Software laboratory 3. 2011.
11
If we execute our tests we will notice that not every test will pass:
The first two input combinations contain zero dividers where the testDivide method received an
unexpected IllegalArgumentException. This is clearly not the fault of the Calculator class. The
problem is with the testDivide method, it should know about the exception.