1. Introduction

JUnit is a tool initially written by
Erich Gamma
and
Kent Beck
.
Gamma was one of the co-authors of
Design Patterns
, collectively known as the "Gang of Four". Beck is the man behind "Extreme Programming" one of the first Agile development methodologies.

2. Problems

I've often heard arguments against the practicality of using JUnit, though in general people do see the advantages. By the way, in my anecdotal experience all of these arguments come from people who are either not using JUnit or use it as a systems test including everything, from the model classes
POJOs
or
EJB's
up and including the database.

2.1. JUnit Requires Extra Effort

Writing tests requires writing supplementary code that should cover each class, each method and all code execution paths. So it would be roughly the same size as the real code. Therefore it requires about double the size of code and 200% of the cost of code written without tests.

This reasoning fails on two accounts:

It does not require a very substantial extra effort, I would estimate between 10% and 20% of the programming effort, when done right.

There are very important benefits, largely exceeding the effort required for writing the tests.

What does require an important extra effort is unit testing tightly coupled code, because you need to set up a lot of 'scaffolding' in order to make the code executable.

The benefits of having unit tests covering a substantial part of the code, are:

The risk of introducing faults when changing code, diminishes significantly.

Promotes loosely coupled and therefore more maintainable and reusable code.

2.2. Unit Tests require regular maintenance

I see the benefits of unit testing, but I find after a certain time, most tests don't work anymore.

The problem is that you need to run the tests at least once a day. Consider introducing a centralized build system or continuous integration setup where the test are executed automatically every day, from your source code repository.

A number of customers I worked with use
Ant scripts
in combination with
Luntbuild
to automatically check-out, compile and run unit tests frequently - at least daily.

3. Testing in Existing Environments

When having a choice, try to use a test driven approach, use
IOC
or refactor until all dependencies are easily handled in unit tests.

If you do not have that option, because you already have code you need to test but are not allowed to refactor/rewrite or because your colleagues haven't seen the light and keep adding tightly coupled code, then - and only then - use mock objects.

3.1. Test Using Mock Objects

When trying to write unit tests for a particular class, we often find that it depends on other classes and heavy weight technology such as EJB's.

A mock object is an object that can replace that complex runtime dependency by something else that is just as simple as possible.

Mock object - place holder for dependency

There are a few libraries that provide 'instant' mock objects. We provide you with some easy to understand examples for:

4. Test Driven Development

In an ideal world, you should start by writing a test and then write the code that implements it. This leads to a different

4.1.

4.2. Loosely Coupled

In this section we will look at how some simple structural changes can make code much easier to test.

4.2.1. Structure

The example shown here is a simple system to organize parcels into shipments.

Imagine a company having trucks that drive once a day from a central warehouse (depot1) to different shops (such as depot2).

A typical situation in a 3-tier architecture is that a client (UI tier code) calls a facade/service (Business tier, an EJB, WS, RMI-object,..) that in turn calls a database...

If we want to test this code, we need to get the facade - ParcelServiceImpl1 - up and running, which implies setting up Hibernate and the database behind it.

Sequence diagram for coupled implementation

Some small changes, however, can make a big difference.

In this case we extracted the basic business logic - for this service and method invocation - into its own class
ShipmentOrganizer
. The result is that we and up with
loosely coupled
or
highly coherent
class implementing the business logic.

Instead of having to setup a database and generating data in a known state before testing, we now provide simply a List of Parcels and can test as much scenarios as we like.

Sequence diagram for loosely coupled implementation

Class diagram of both implementations

4.2.2. Code

This code is a simplified but working example of a pattern that often occurs in real world systems. In order to run the first implementation, you would have to provide a database and a Hibernate configuration, but that is out of scope for this example.