JUnit is the de facto standard unit testing library for the Java language. JUnit 4 is the first significant release of this library in almost three years. It promises to simplify testing by exploiting Java 5's annotation feature to identify tests rather than relying on subclassing, reflection, and naming conventions. In this article, obsessive code tester Elliotte Harold takes JUnit 4 out for a spin and details how to use the new framework in your own work. Note that this article assumes prior experience with JUnit.
JUnit, developed by Kent Beck and Erich Gamma, is almost indisputably the single most important third-party Java library ever developed. As Martin Fowler has said, "Never in the field of software development was so much owed by so many to so few lines of code." JUnit kick-started and then fueled the testing explosion. Thanks to JUnit, Java code tends to be far more robust, reliable, and bug free than code has ever been before. JUnit (itself inspired by Smalltalk's SUnit) has inspired a whole family of xUnit tools bringing the benefits of unit testing to a wide range of languages. nUnit (.NET), pyUnit (Python), CppUnit (C++), dUnit (Delphi), and others have test-infected programmers on a multitude of platforms and languages.
However, JUnit is just a tool. The real benefits come from the ideas and techniques embodied by JUnit, not the framework itself. Unit testing, test-first programming, and test-driven development do not have to be implemented in JUnit, any more than GUI programming must be done with Swing. JUnit itself was last updated almost three years ago. Although it's proved more robust and longer lasting than most frameworks, bugs have been found; and, more importantly, Java has moved on. The language now supports generics, enumerations, variable length argument lists, and annotations--features that open up new possibilities for reusable framework design.
JUnit's stasis has not been missed by programmers who are eager to dethrone it. Challengers range from Bill Venners's Artima SuiteRunner to Cedric Beust's TestNG. These libraries have some features to recommend them, but none have achieved the mind or market share held by JUnit. None have broad out-of-the-box support in products like Ant, Maven, or Eclipse. So Beck and Gamma have commenced work on an updated version of JUnit that takes advantage of the new features of Java 5 (especially annotations) to make unit testing even simpler than it was with the original JUnit. According to Beck, "The theme of JUnit 4 is to encourage more developers to write more tests by further simplifying JUnit." Although it maintains backwards compatibility with existing JUnit 3.8 test suites, JUnit 4 promises to be the most significant innovation in Java unit testing since JUnit 1.0.
11. How do I use a test fixture?
A test fixture is useful if you have two or more tests for a common set of objects. Using a test fixture avoids duplicating the code necessary to initialize (and cleanup) the common objects.
Tests can use the objects (variables) in a test fixture, with each test invoking different methods on objects in the fixture and asserting different expected results. Each test runs in its own test fixture to isolate tests from the changes made by other tests. That is, tests don't share the state of objects in the test fixture. Because the tests are isolated, they can be run in any order.
To create a test fixture, declare instance variables for the common objects. Initialize these objects in a public void method annotated with @Before. The JUnit framework automatically invokes any @Before methods before each test is run.
The following example shows a test fixture with a common Collection object.
package junitfaq;
import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;
public class SimpleTest {
private Collection
12. How do I write a test that fails when an unexpected exception is thrown?
Declare the exception in the throws clause of the test method and don't catch the exception within the test method. Uncaught exceptions will cause the test to fail with an error.
The following is an example test that fails when the IndexOutOfBoundsException is raised:
@Test
public void testIndexOutOfBoundsExceptionNotRaised()
throws IndexOutOfBoundsException {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
13. How do I write a test that passes when an expected exception is thrown?
Add the optional expected attribute to the @Test annotation. The following is an example test that passes when the expected IndexOutOfBoundsException is raised:
@Test(expected=IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
14. How do I write and run a simple test?
A;Create a class:
package junitfaq;
import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;
public class SimpleTest {
Write a test method (annotated with @Test) that asserts expected results on the object under test:
@Test
public void testEmptyCollection() {
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
If you are running your JUnit 4 tests with a JUnit 3.x runner, write a suite() method that uses the JUnit4TestAdapter class to create a suite containing all of your test methods:
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(SimpleTest.class);
}
Although writing a main() method to run the test is much less important with the advent of IDE runners, it's still possible:
public static void main(String args[]) {
org.junit.runner.JUnitCore.main("junitfaq.SimpleTest");
}
}
Run the test:
To run the test from the console, type:
java org.junit.runner.JUnitCore junitfaq.SimpleTest
To run the test with the test runner used in main(), type:
java junitfaq.SimpleTest
The passing test results in the following textual output:
.
Time: 0
OK (1 tests)
15. How To Compile a JUnit Test Class?
Compiling a JUnit test class is like compiling any other Java classes. The only thing you need watch out is that the JUnit JAR file must be included in the classpath. For example, to compile the test class HelloTest.java described previously, you should do this:
javac -cp junit-4.4.jar HelloTest.java
dir HelloTest.*
453 HelloTest.class
183 HelloTest.java
The compilation is ok, if you see the HelloTest.class file.
16. How To Wirte a Simple JUnit Test Class?
This is a common test in a job interview. You should be able to write this simple test class with one test method:
import org.junit.*;
public class HelloTest {
@Test public void testHello() {
String message = "Hello World!";
Assert.assertEquals(12, message.length());
}
}
17. Is there a basic template I can use to create a test?
The following templates are a good starting point. Copy/paste and edit these templates to suit your coding style.
SampleTest is a basic test template:
import org.junit.*;
import static org.junit.Assert.*;
public class SampleTest {
private java.util.List emptyList;
/**
* Sets up the test fixture.
* (Called before every test case method.)
*/
@Before
public void setUp() {
emptyList = new java.util.ArrayList();
}
/**
* Tears down the test fixture.
* (Called after every test case method.)
*/
@After
public void tearDown() {
emptyList = null;
}
@Test
public void testSomeBehavior() {
assertEquals("Empty list should have 0 elements", 0, emptyList.size());
}
@Test(expected=IndexOutOfBoundsException.class)
public void testForException() {
Object o = emptyList.get(0);
}
}
18. Under what conditions should I not test get() and set() methods?
Most of the time, get/set methods just can't break, and if they can't break, then why test them? While it is usually better to test more, there is a definite curve of diminishing returns on test effort versus "code coverage". Remember the maxim: "Test until fear turns to boredom."
Assume that the getX() method only does "return x;" and that the setX() method only does "this.x = x;". If you write this test:
@Test
public void testGetSetX() {
setX(23);
assertEquals(23, getX());
}
then you are testing the equivalent of the following:
@Test
public void testGetSetX() {
x = 23;
assertEquals(23, x);
}
or, if you prefer,
@Test
public void testGetSetX() {
assertEquals(23, 23);
}
At this point, you are testing the Java compiler, or possibly the interpreter, and not your component or application. There is generally no need for you to do Java's testing for them.
If you are concerned about whether a property has already been set at the point you wish to call getX(), then you want to test the constructor, and not the getX() method. This kind of test is especially useful if you have multiple constructors:
@Test
public void testCreate() {
assertEquals(23, new MyClass(23).getX());
}
19. Under what conditions should I test get() and set() methods?
Unit tests are intended to alleviate fear that something might break. If you think a get() or set() method could reasonably break, or has in fact contributed to a defect, then by all means write a test.
In short, test until you're confident. What you choose to test is subjective, based on your experiences and confidence level.
20. Under What Conditions Should You Not Test Get() and Set() Methods?
The JUnit FAQ provides a good answer to this question:
Most of the time, get/set methods just can't break, and if they can't break, then why test them? While it is usually better to test more, there is a definite curve of diminishing returns on test effort versus "code coverage". Remember the maxim: "Test until fear turns to boredom."
Assume that the getX() method only does "return x;" and that the setX() method only does "this.x = x;". If you write this test:
@Test
public void testGetSetX() {
setX(23);
assertEquals(23, getX());
}
then you are testing the equivalent of the following:
@Test
public void testGetSetX() {
x = 23;
assertEquals(23, x);
}
or, if you prefer,
@Test
public void testGetSetX() {
assertEquals(23, 23);
}
At this point, you are testing the Java compiler, or possibly the interpreter, and not your component or application. There is generally no need for you to do Java's testing for them.
If you are concerned about whether a property has already been set at the point you wish to call getX(), then you want to test the constructor, and not the getX() method. This kind of test is especially useful if you have multiple constructors:
@Test
public void testCreate() {
assertEquals(23, new MyClass(23).getX());
}