Software Testing for Programmers, Part 2

Whenever you are tempted to type something into a print statement or a debugger expression, write it as a test instead.

—Martin Fowler

In my previous article on software testing—Software Testing for Programmers, Part 1—I presented the basics of software testing for programmers. In this article, I illustrate how to do unit testing of Java applications with JUnit, an open source framework for testing Java programs.

JUnit

The core of JUnit is a set of classes and interfaces that provides an easy-to-use framework for building automated tests. It is comprised of the following:

TestCase

Defines tests and fixtures

Test

Defines the interface for running a test

TestSuite

Collects and runs a set of tests

Assert

Provides a set of assertion methods

TestRunner

Runs Tests and TestSuites

Tests

Tests are defined in test cases. To create a test case:

Create a class that extends junit.framework.TestCase.

Write at least one method that starts with the word "test."

For example, suppose that you are developing an application to help you make homebrew (that's right, home-brewed beer), and in the application you have a class that models a batch of brew and calculates alcohol by volume (ABV). Here is the code for the ABV calculation:

Assertion methods are defined in junit.framework.Assert and are inherited by TestCase. To implement an assertion, call the assertion method that is applicable to the expected condition. A message may be specified to provide details about the assertion if it fails.

Here are some examples of assertions:

// Assert that a given value equals an expected value:
assertEquals(1, x);
// Assert that a condition is true:
assertTrue("ABV exceeds 1.0", abv > 1.0);
// Assert that a condition is false:
assertFalse(abv > 1.0);
// Assert that an object is null:
assertNull("someObject is null.", someObject);
// Assert that an object is not null:
assertNotNull(someObject);
// Assert that two variables refer to the same object:
assertSame(obj1, obj2);
// Assert that two variables do not refer to the
// same object:
assertNotSame(obj1, obj2);

Exceptions

Testing for expected exceptions is done by catching the exception and forcing failure, by calling fail, if the exception is not thrown. As an example, here is the AbvTest test case from above with some exception tests added:

Fixtures

Fixtures are implemented in test cases and are used to set up and tear down objects that are common to the tests in the test case. To implement a fixture:

Define private variables for the items in the fixture.

Override setUp() to initialize the fixture items.

Override tearDown to close any resources, such as files or database connections, that are opened in setUp().

Before a test is run, setUp() is called. After the test completes, whether it passed or failed, tearDown() is called. Each test gets a new fixure, so that one test does not cause side effects in another test.

Continuing with the homebrew example, suppose you add a method to Brew that calculates the bitterness of your brew. Here is the code: