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.

3 comments:

Anonymous said...

Hi Brian,

The normal way to use mocks is something like:

public class MockDevice extends MockObject implements Device {
...
}

Without this you lose the auto-verify functionality of MockObjects. Also some dynamic Mock libraries require interfaces; I'm told this is true of EasyMock and believe JMock's similar.

Even further; the danger of extending a real class in your MockObject is that the class under test might end up calling a real method you didn't know about. With an interface, that doesn't happen - if you don't mock the method, you get an error.

You can certainly get around this by using a factory or a registry if you only ever need one of them:

public class DeviceRegistry() {
private Device device;

public Device getDevice() {
if (device == null) {
device = new DeviceImpl();
}
return device;
}
}

You can then use reflection as before to make device be a MockDevice. Add registerDevice methods etc. if it seems appropriate. This is on the limit of stuff I know, so others may disagree or improve, but I hope it's helpful. It's what I'd do now I've seen how great dynamic mocks are (if I couldn't use inversion of control, anyway).

Anonymous said...

Sorry, should have told you who was posting that advice so you know who to blame if it's wrong. Anonymous posts wind me up. - Liz

Brian Oxley said...

I'm familiar with the technique you describe -- it's what I recommend to my coworkers. :-) I was looking for a minimal change in the existing code base to pull off the trick of stubbing out calls to a physical device.

I want to make the distinction between a stub and a mock. A mock is programmable and supports verification of calls (as you mention). But stub I mean something rather dumber which is 'just enough' to get buy, but no more.

When I revisit the code base, I plan on going for IoC whole hog, using interfaces, and fixing the problem root and stock.

Thanks for commenting!