Introducing Unit Testing

As the name suggests, unit testing tests the components of your applications on their own. It allows you to prove that a single class works in terms of contract checking and functionality.

Contract checking means that the class should check that it is called within specifications. Let's assume a class with a method returns a square root of a real (double) number. If the argument passed to the function is negative or NaN, the method throws an IllegalArgumentException.

The unit test for such a class should test the contract by calling the method with a negative argument and then with a NaN. In both cases, an IllegalArgumentException must be thrown. Once we have tested the contract, we must also test the functionality. Using our square root method, we call it with 9 and then check that the returned value is 3.[1]

Unfortunately, we rarely test such simple methods; in most cases, the tests are far more complex, making it very easy to miss a situation that might cause the tested class to fail. This is not a problem of unit testing as such; it is a problem for the programmer who writes incomplete unit tests. Later in the chapter, we discuss the ways to make sure that the unit tests cover all the code.

To simplify the task of writing the unit tests, we use existing unit test frameworks, such as JUnit. You can download the latest version of JUnit from www.junit.org/index.htm; if you are using a Java IDE such as Eclipse, JBuilder, or others, chances are that JUnit is already bundled with the IDE. Here, we cover JUnit usage in Eclipse.

Let's write our first JUnit test in Eclipse; Listing A-1 shows that the code of the unit test always succeeds because it is not actually testing anything.

Listing A-1: JUnit Test

image from book
package com.apress.propspring.apa;      import junit.framework.TestCase;      public class TestAll extends TestCase {     public void testFoo() {              } }
image from book

The individual JUnit tests are subclasses of TestCase, which offers basic assertion and failure methods. The failure methods are often used in contract testing, whereas the assertion methods are used in functionality testing. The individual tests are simple public void methods whose names begin with test. Each test method is allowed to throw Exception.

To see our test for our imaginary square root method, see Listing A-2.

Listing A-2: Test for the Square Root Method

image from book
public void testSqrt() {     try {         sqrt(-1);         fail("This function returns only real results");     } catch (IllegalArgumentException expected) {         // OK     }          try {         sqrt(Double.NaN);         fail("This function cannot take +/-NaN as argument");     } catch (IllegalArgumentException expected) {         // OK     }          assertEquals(3.0, sqrt(9), 0); } 
image from book

As you can see, the test for the simple sqrt() method is much longer than the actual implementation, but once it is tested, we can be sure that the sqrt() method functions correctly and that any errors are handled correctly. Listing A-2 also demonstrates how to use the fail() and assert*() methods to perform the testing.

In most cases, the classes being tested (targets) require other objects to function. Imagine a unit test for a piece of business logic—it probably requires data access classes to retrieve and store the data it processes. Let's imagine you are testing a discount calculation that applies a discount to an order based on the value of the order and the value of the previous orders. You can use the data access layer that takes the data from the database, but this is rarely a good solution. The unit test does not test the business component in isolation; instead it tests the data access components as well as the database. Furthermore, the tables in the database must contain suitable data to test all nuances of the discount calculation algorithm. This makes the test very fragile: a change in the data may influence the result of the test.

It is better to create mock classes for the target's dependencies. In the case of the discount calculation business component, you probably have to create a mock order data access component. Let's assume that OrderDao is an interface for the data access component that is used throughout the application. It is easy to provide an implementation of OrderDao so that it always returns the correct data for the test without accessing the database. This way the unit test is completely isolated and allows you to run repeated tests without influencing any other components of your application.

[1] Note that doubles cannot represent certain numbers precisely, hence the test should allow for some tolerance. This tolerance is determined by the expected usage of the method we are testing.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net