Software QA FYI - SQAFYI

JUnit Test Infected: Programmers Love Writing Tests

By:

Testing is not closely integrated with development. This prevents you from measuring the progress of development- you can't tell when something starts working or when something stops working. Using JUnit you can cheaply and incrementally build a test suite that will help you measure your progress, spot unintended side effects, and focus your development efforts.

Contents
* The Problem
* Example
* Testing Practices
* Conclusions


The Problem
Every programmer knows they should write tests for their code. Few do. The universal response to "Why not?" is "I'm in too much of a hurry." This quickly becomes a vicious cycle- the more pressure you feel, the fewer tests you write. The fewer tests you write, the less productive you are and the less stable your code becomes. The less productive and accurate you are, the more pressure you feel.

Programmers burn out from just such cycles. Breaking out requires an outside influence. We found the outside influence we needed in a simple testing framework that lets us do a little testing that makes a big difference.

The best way to convince you of the value of writing your own tests would be to sit down with you and do a bit of development. Along the way, we would encounter new bugs, catch them with tests, fix them, have them come back, fix them again, and so on. You would see the value of the immediate feedback you get from writing and saving and rerunning your own unit tests.

Unfortunately, this is an article, not an office overlooking charming old-town Zürich, with the bustle of medieval commerce outside and the thump of techno from the record store downstairs, so we'll have to simulate the process of development. We'll write a simple program and its tests, and show you the results of running the tests. This way you can get a feel for the process we use and advocate without having to pay for our presence.

Example
As you read, pay attention to the interplay of the code and the tests. The style here is to write a few lines of code, then a test that should run, or even better, to write a test that won't run, then write the code that will make it run.

The program we write will solve the problem of representing arithmetic with multiple currencies. Arithmetic between single currencies is trivial, you can just add the two amounts. Simple numbers suffice. You can ignore the presence of currencies altogether.

Things get more interesting once multiple currencies are involved. You cannot just convert one currency into another for doing arithmetic since there is no single conversion rate- you may need to compare the value of a portfolio at yesterday's rate and today's rate.

Let's start simple and define a class Money to represent a value in a single currency. We represent the amount by a simple int. To get full accuracy you would probably use double or java.math.BigDecimal to store arbitrary-precision signed decimal numbers. We represent a currency as a string holding the ISO three letter abbreviation (USD, CHF, etc.). In more complex implementations, currency might deserve its own object.

class Money {
private int fAmount;
private String fCurrency;

public Money(int amount, String currency) {
fAmount= amount;
fCurrency= currency;
}

public int amount() {
return fAmount; } public String currency() { return fCurrency;
}
}


When you add two Moneys of the same currency, the resulting Money has as its amount the sum of the other two amounts.

public Money add(Money m) {
return new Money(amount()+m.amount(), currency());
}

Now, instead of just coding on, we want to get immediate feedback and practice "code a little, test a little, code a little, test a little". To implement our tests we use the JUnit framework. To write tests you need to get the latest copy JUnit (or write your own equivalent- it's not so much work).

JUnit defines how to structure your test cases and provides the tools to run them. You implement a test in a subclass of TestCase. To test our Money implementation we therefore define MoneyTest as a subclass of TestCase. In Java, classes are contained in packages and we have to decide where to put MoneyTest. Our current practice is to put MoneyTest in the same package as the classes under test. In this way a test case has access to the package private methods. We add a test method testSimpleAdd, that will exercise the simple version of Money.add() above. A JUnit test method is an ordinary method without any parameters.

public class MoneyTest extends TestCase {
//…
public void testSimpleAdd() {
Money m12CHF= new Money(12, "CHF"); // (1)
Money m14CHF= new Money(14, "CHF");
Money expected= new Money(26, "CHF");
Money result= m12CHF.add(m14CHF); // (2)
Assert.assertTrue(expected.equals(result)); // (3)
}
}

The testSimpleAdd() test case consists of:
1. Code which creates the objects we will interact with during the test. This testing context is commonly referred to as a test's fixture. All we need for the testSimpleAdd test are some Money objects.
2. Code which exercises the objects in the fixture.
3. Code which verifies the result.

Before we can verify the result we have to digress a little since we need a way to test that two Money objects are equal. The Java idiom to do so is to override the method equals defined in Object. Before we implement equals let's a write a test for equals in MoneyTest.
public void testEquals() {
Money m12CHF= new Money(12, "CHF");
Money m14CHF= new Money(14, "CHF");

Assert.assertTrue(!m12CHF.equals(null));
Assert.assertEquals(m12CHF, m12CHF);
Assert.assertEquals(m12CHF, new Money(12, "CHF")); // (1)
Assert.assertTrue(!m12CHF.equals(m14CHF));
}

The equals method in Object returns true when both objects are the same. However, Money is a value object. Two Monies are considered equal if they have the same currency and value. To test this property we have added a test (1) to verify that Monies are equal when they have the same value but are not the same object.

Next let's write the equals method in Money:
public boolean equals(Object anObject) {
if (anObject instanceof Money) {
Money aMoney= (Money)anObject;
return aMoney.currency().equals(currency())
&& amount() == aMoney.amount();
}
return false;
}

Since equals can receive any kind of object as its argument we first have to check its type before we cast it as a Money. As an aside, it is a recommended practice to also override the method hashCode whenever you override method equals. However, we want to get back to our test case.

With an equals method in hand we can verify the outcome of testSimpleAdd. In JUnit you do so by a calling Assert.assertTrue, which triggers a failure that is recorded by JUnit when the argument isn't true. Since assertions for equality are very common, there is also an Assert.assertEquals convenience method. In addition to testing for equality with equals, it reports the printed value of the two objects in the case they differ. This lets us immediately see why a test failed in a JUnit test result report. The value a string representation created by the toString converter method. There are other asertXXXX variants not discussed here.

Now that we have implemented two test cases we notice some code duplication for setting-up the tests. It would be nice to reuse some of this test set-up code. In other words, we would like to have a common fixture for running the tests. With JUnit you can do so by storing the fixture's objects in instance variables of your TestCase subclass and initialize them by overridding the setUp method. The symmetric operation to setUp is tearDown which you can override to clean up the test fixture at the end of a test. Each test runs in its own fixture and JUnit calls setUp and tearDown for each test so that there can be no side effects among test runs.

public class MoneyTest extends TestCase {
private Money f12CHF;
private Money f14CHF;

protected void setUp() {
f12CHF= new Money(12, "CHF");
f14CHF= new Money(14, "CHF");
}
}

We can rewrite the two test case methods, removing the common setup code:
public void testEquals() {
Assert.assertTrue(!f12CHF.equals(null));
Assert.assertEquals(f12CHF, f12CHF);
Assert.assertEquals(f12CHF, new Money(12, "CHF"));
Assert.assertTrue(!f12CHF.equals(f14CHF));
}

public void testSimpleAdd() {
Money expected= new Money(26, "CHF");
Money result= f12CHF.add(f14CHF);
Assert.assertTrue(expected.equals(result));
}

Two additional steps are needed to run the two test cases:
1. define how to run an individual test case,
2. define how to run a test suite.

JUnit supports two ways of running single tests:
* static
* dynamic

In the static way you override the runTest method inherited from TestCase and call the desired test case. A convenient way to do this is with an anonymous inner class. Note that each test must be given a name, so you can identify it if it fails.
TestCase test= new MoneyTest("simple add") {
public void runTest() {
testSimpleAdd();
}
};
A template method[1] in the superclass will make sure runTest is executed when the time comes.
The dynamic way to create a test case to be run uses reflection to implement runTest. It assumes the name of the test is the name of the test case method to invoke. It dynamically finds and invokes the test method. To invoke the testSimpleAdd test we therefore construct a MoneyTest as shown below:
TestCase test= new MoneyTest("testSimpleAdd");
The dynamic way is more compact to write but it is less static type safe. An error in the name of the test case goes unnoticed until you run it and get a NoSuchMethodException. Since both approaches have advantages, we decided to leave the choice of which to use up to you.
As the last step to getting both test cases to run together, we have to define a test suite. In JUnit this requires the definition of a static method called suite. The suite method is like a main method that is specialized to run tests. Inside suite you add the tests to be run to a TestSuite object and return it. A TestSuite can run a collection of tests. TestSuite and TestCase both implement an interface called Test which defines the methods to run a test. This enables the creation of test suites by composing arbitrary TestCases and TestSuites. In short TestSuite is a Composite [1]. The code below illustrates the creation of a test suite with the dynamic way to run a test.
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTest(new MoneyTest("testEquals"));
suite.addTest(new MoneyTest("testSimpleAdd"));
return suite;
}

Since JUnit 2.0 there is an even simpler dynamic way. You only pass the class with the tests to a TestSuite and it extracts the test methods automatically.
public static Test suite() {
return new TestSuite(MoneyTest.class);
}

Here is the corresponding code using the static way.
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTest(
new MoneyTest("money equals") {
protected void runTest() { testEquals(); }
}
);

suite.addTest(
new MoneyTest("simple add") {
protected void runTest() { testSimpleAdd(); }
}
);
return suite;
}

Now we are ready to run our tests. JUnit comes with a graphical interface to run tests. Type the name of your test class in the field at the top of the window. Press the Run button. While the test is run JUnit shows its progress with a progress bar below the input field. The bar is initially green but turns into red as soon as there is an unsuccessful test. Failed tests are shown in a list at the bottom. Figure 1 shows the TestRunner window after we run our trivial test suite.

Full article...


Other Resource

... to read more articles, visit http://sqa.fyicenter.com/art/

JUnit Test Infected: Programmers Love Writing Tests