|
|
|
What is JUnit?
|
JUnit is a simple, open source
framework to write and run repeatable tests. It is an instance of the
xUnit architecture for unit testing frameworks. JUnit features include:
Assertions for testing expected results
Test fixtures for sharing common test data
Test runners for running tests
|
How
do I install JUnit?
|
First, download the latest
version of JUnit, referred to below as junit.zip.
Then install JUnit on your platform of choice:
Windows
To install JUnit on Windows, follow these steps:
Unzip the junit.zip distribution file to a directory referred to as
%JUNIT_HOME%.
Add JUnit to the classpath:
set CLASSPATH=%CLASSPATH%;%JUNIT_HOME%\junit.jar
Unix (bash)
To install JUnit on Unix, follow these steps:
Unzip the junit.zip distribution file to a directory referred to as
$JUNIT_HOME.
Add JUnit to the classpath:
export CLASSPATH=$CLASSPATH:$JUNIT_HOME/junit.jar
(Optional) Unzip the $JUNIT_HOME/src.jar file.
Test the installation by running the sample tests distributed with
JUnit. Note that the sample tests are located in the installation
directory directly, not the junit.jar file. Therefore, make sure that
the JUnit installation directory is on your CLASSPATH. Then simply
type:
java org.junit.runner.JUnitCore org.junit.tests.AllTests
All the tests should pass with an "OK" message.
If the tests don't pass, verify that junit.jar is in the CLASSPATH.
|
How
do I write and run a simple test?
|
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)
|
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<Object> collection;
@Before
public void setUp() {
collection = new ArrayList<Object>();
}
@Test
public void
testEmptyCollection() {
assertTrue(collection.isEmpty());
}
@Test
public void
testOneItemCollection() {
collection.add("itemA");
assertEquals(1, collection.size());
}
}
Given this test, the methods might
execute in the following order:
setUp()
testEmptyCollection()
setUp()
testOneItemCollection()
The ordering of test-method invocations is not guaranteed, so
testOneItemCollection() might be executed before testEmptyCollection().
But it doesn't matter, because each method gets its own instance of the
collection.
Although JUnit provides a new instance of the fixture objects for each
test method, if you allocate any external resources in a @Before
method, you should release them after the test runs by annotating a
method with @After. The JUnit framework automatically invokes any
@After methods after each test is run. For example:
package junitfaq;
import org.junit.*;
import static org.junit.Assert.*;
import java.io.*;
public class OutputTest {
private File output;
@Before
public void
createOutputFile() {
output = new File(...);
}
@After
public void
deleteOutputFile() {
output.delete();
}
@Test
public void
testSomethingWithFile() {
...
}
}
With this test, the methods will execute in the following order:
createOutputFile()
testSomethingWithFile()
deleteOutputFile()
|
How
do I test a method that doesn't return anything?
|
Often if a method doesn't return
a value, it will have some side effect. Actually, if it doesn't return
a value AND doesn't have a side effect, it isn't doing anything.
There may be a way to verify that the side effect actually occurred as
expected. For example, consider the add() method in the Collection
classes. There are ways of verifying that the side effect happened
(i.e. the object was added). You can check the size and assert that it
is what is expected:
@Test
public void testCollectionAdd() {
Collection collection = new
ArrayList();
assertEquals(0,
collection.size());
collection.add("itemA");
assertEquals(1,
collection.size());
collection.add("itemB");
assertEquals(2,
collection.size());
}
Another approach is to make use of
MockObjects.
A related issue is to design for testing. For example, if you have a
method that is meant to output to a file, don't pass in a filename, or
even a FileWriter. Instead, pass in a Writer. That way you can pass in
a StringWriter to capture the output for testing purposes. Then you can
add a method (e.g. writeToFileNamed(String filename)) to encapsulate
the FileWriter creation.
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.
|
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.
|
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());
}
|
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);
}
|
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);
}
|
How
do I test protected methods?
|
Place your tests in the same
package as the classes under test.
|
Why
does JUnit only report the first failure in a single test?
|
Reporting multiple failures in a
single test is generally a sign that the test does too much, compared
to what a unit test ought to do. Usually this means either that the
test is really a functional/acceptance/customer test or, if it is a
unit test, then it is too big a unit test.
JUnit is designed to work best with a number of small tests. It
executes each test within a separate instance of the test class. It
reports failure on each test. Shared setup code is most natural when
sharing between tests. This is a design decision that permeates JUnit,
and when you decide to report multiple failures per test, you begin to
fight against JUnit. This is not recommended.
Long tests are a design smell and indicate the likelihood of a design
problem. Kent Beck is fond of saying in this case that "there is an
opportunity to learn something about your design." We would like to see
a pattern language develop around these problems, but it has not yet
been written down.
Finally, note that a single test with multiple assertions is isomorphic
to a test case with multiple tests:
One test method, three assertions:
public class MyTestCase {
@Test
public void testSomething() {
// Set up for the test,
manipulating local variables
assertTrue(condition1);
assertTrue(condition2);
assertTrue(condition3);
}
}
Three test methods, one assertion each:
public class MyTestCase {
// Local variables become instance variables
@Before
public void setUp() {
// Set up for the test,
manipulating instance variables
}
@Test
public void testCondition1() {
assertTrue(condition1);
}
@Test
public void testCondition2() {
assertTrue(condition2);
}
@Test
public void testCondition3() {
assertTrue(condition3);
}
}
The resulting tests use JUnit's natural
execution and reporting mechanism and, failure in one test does not
affect the execution of the other tests. You generally want exactly one
test to fail for any given bug, if you can manage it.
|
How
do I test things that must be run in a J2EE container (e.g. servlets,
EJBs)?
|
Refactoring J2EE components to
delegate functionality to other objects that don't have to be run in a
J2EE container will improve the design and testability of the software.
Cactus is an open source JUnit extension that can be used to test J2EE
components in their natural environment.
|
Do
I need to write a test class for every class I need to test?
|
No. It is a convention to start
with one test class per class under test, but it is not necessary.
Test classes only provide a way to organize tests, nothing more.
Generally you will start with one test class per class under test, but
then you may find that a small group of tests belong together with
their own common test fixture.[1] In this case, you may move those
tests to a new test class. This is a simple object-oriented
refactoring: separating responsibilities of an object that does too
much.
Another point to consider is that the TestSuite is the smallest
execution unit in JUnit: you cannot execute anything smaller than a
TestSuite at one time without changing source code. In this case, you
probably do not want to put tests in the same test class unless they
somehow "belong together". If you have two groups of tests that you
think you'd like to execute separately from one another, it is wise to
place them in separate test classes.
[1] A test fixture is a common set of test data and collaborating
objects shared by many tests. Generally they are implemented as
instance variables in the test class.
|
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);
}
}
|
When
should tests be written?
|
Tests should be written before
the code. Test-first programming is practiced by only writing new code
when an automated test is failing.
Good tests tell you how to best design the system for its intended use.
They effectively communicate in an executable format how to use the
software. They also prevent tendencies to over-build the system based
on speculation. When all the tests pass, you know you're done!
Whenever a customer test fails or a bug is reported, first write the
necessary unit test(s) to expose the bug(s), then fix them. This makes
it almost impossible for that particular bug to resurface later.
|
Why
not just use System.out.println()?
|
Inserting debug statements into
code is a low-tech method for debugging it. It usually requires that
output be scanned manually every time the program is run to ensure that
the code is doing what's expected.
It generally takes less time in the long run to codify expectations in
the form of an automated JUnit test that retains its value over time.
If it's difficult to write a test to assert expectations, the tests may
be telling you that shorter and more cohesive methods would improve
your design.
|
The Simple Object Access
Protocol (SOAP) uses XML to define a protocol for the exchange of
information in distributed computing environments. SOAP
consists of three components: an envelope, a set of encoding rules, and
a convention for representing remote procedure calls.
|