Saturday, December 11, 2004

The easy way

While updating Java code to permit replacement of one implementation of a system-wide object with another (in this case, we have a physical device which is needed for production code, but which is a problem to require for unit tests — I want to use a mock or stub implementation for testing to avoid accessing a real device)

I started down the inversion of control path and faced a daunting problem: large portions of the system needed updating to support appropriate constructors, new class members to hold the IoC objects, rewritten callers to cope with the new constructors, ad nauseum. Yikes!

What to do?

After some tinkering, I decided to give up on the "pure" solution and hack something just for the immediate problem at hand. Rather than adjust code all through out the system (and try to adjust the culture around me to accept those changes "just for testing"), I limited the scope of change to just those places which actually need a device object.

First I implemented the singleton pattern for the device class, then refactored all the users to call Device.newInstance() instead of new Device().

Next, I made sure the singleton implementation used lazy initialization:

private static Device SINGLETON;

public static Device newInstance() {
    if (null == SINGLETON)
        SINGLETON = new Device();

    return SINGLETON;
}

There's the crux: now testing code can put a stub or mock implementation into the SINGLETON field with reflection in setUp() and replace it with null in tearDown(). This ensures that tests see only a stub or mock implementation anywhere in the production code which uses a device object. And no code need know any details about who uses a device object or how.

Finally, tearDown sees to it that other tests get a real device implementation if they need one since it sets the singleton back to null (see the code above).

The only other update to the device object is to make the constructor protected so that a stub or mock implementation can extend the class.

Post a Comment