Overiding Singletons For Testing
While thumbing through the Xunit Test Patterns book tonight, I came across a rather interesting pattern named “Dependency Lookup.” The premise was simple, and I had actually used it on occasion… you delegate object retrieval/creation to a broker rather than access it directly (i.e. factory), and then provide a mechanism to set the object you want to have returned (good for overriding dependencies with mocks/stubs). Normal stuff, I know… but what interested me was one implementation that used a Singleton, but then the test extended that Singleton and overrode the sole instance in order to return test data.
What follows is my late night, 10 minute hack to exercise the idea. ![]()
I thought I’d try it with a quick example to exercise the idea, by having an object similar to one mentioned in the book’s example, TimeDisplay, which displays interesting messages based on what time it is. To start, here’s a test:
public class TimeDisplayTest {
@Test
public void itsLunchTime() throws ParseException{
//-- exercise
TimeDisplay sut = new TimeDisplay();
assertEquals("It's Lunch Time!", sut.getMessage());
}
}
And accompanying, “working” code:
public class TimeDisplay {
private static SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
public String getMessage() {
String currentTime = formatter.format(new Date());
if(currentTime.equals("12:00:00")){
return "It's Lunch Time!";
}
return currentTime;
}
}
Now, it actually works, but we aren’t going to be able to get that test to pass at all unless someone runs it at the beginning of their lunch break, now are we? Another problem is that the test doesn’t really convey what time it needs to be for lunch (we can assume, but there’s no way of knowing without looking in the code). In the past, I would have gotten away with making this work with some extensive mocking and stubbing…. most likely I’d delegate the retrieval of the current Date to an internal method, then stubbed it out to return a doctored Date… with some complaining and moaning that I cheated.
An alternative solution would be to delegate retrieval of the current Date to a DateProvider, which looks something like this:
public class DateProvider {
protected static DateProvider soleInstance = null;
public static DateProvider getInstance(){
if(soleInstance == null){
soleInstance = new DateProvider();
}
return soleInstance;
}
public Date getCurrentDate() {
return new Date();
}
}
Notice how the soleInstance is protected? We’ll look back at why in a minute. Next I replace the date creation used in TimeDisplay with a call to the DateProvider:
Date date = DateProvider.getInstance().getCurrentDate(); String currentTime = formatter.format(date);
Of course… the test still fails, even though the code is technically sound and will work when it’s lunch time. Now here’s where things get interesting. To allow the test to set a date for the current date, we can simply extend DateProvider and override soleInstance:
public class TestDateProvider extends DateProvider {
private Date date;
private TestDateProvider(Date presetDate){
this.date = presetDate;
}
public static void overideSoleInstance(Date presetDate){
soleInstance = new TestDateProvider(presetDate);
}
public Date getCurrentDate(){
return this.date;
}
}
Now I’m left to just modifying my test to create a Date object set to 12:00:00, set it as the currentDate returned, and exercise the system under test (TimeDisplay):
public class TimeDisplayTest {
@Test
public void itsLunchTime() throws ParseException{
DateFormat format = new SimpleDateFormat("HH:mm:ss");
Date stubbedDate = format.parse("12:00:00");
TestDateProvider.overideSoleInstance(stubbedDate);
//-- exercise
TimeDisplay sut = new TimeDisplay();
assertEquals("It's Lunch Time!", sut.getMessage());
}
}
The test now passes… and just to be sure, I try add a test with an invalid time to ensure that I don’t have a false positive (and at the same time removing some of the code duplication):
public class TimeDisplayTest {
@Test
public void itsLunchTime() throws ParseException{
TestDateProvider.overideSoleInstance(date("12:00:00"));
//-- exercise
TimeDisplay sut = new TimeDisplay();
assertEquals("It's Lunch Time!", sut.getMessage());
}
@Test
public void doesNotDisplayLunchTimeForOtherTimes() throws ParseException{
TestDateProvider.overideSoleInstance(date("24:00:00"));
//-- exercise
TimeDisplay sut = new TimeDisplay();
assertFalse("It's Lunch Time!".equals(sut.getMessage()));
}
private Date date(String time) throws ParseException {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
Date date = format.parse(time);
return date;
}
}
That’s quite useful. I would be tempted to include the TestDateProvider inside the unit test itself, but I personally don’t like clutter in my unit tests that draw attention away from intent. I like this approach as it feels pretty clean and can easily fit anywhere a singleton method is used. Honestly… I never would have thought of overriding a Singleton.
Neat stuff. ![]()
If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!








July 7th, 2007 at 2:08 am
I was doing something very similar last week to get some tests working. I ended up actually using a ThreadLocal singleton with a private setter rather than an a subclass.
The tests pass in an anonymous inner implementation specific to the test at hand via a simple helper method:
final String[] reminderSmsMessage = new String[1];
BindUtils.bindSmsSender(new SendSMS() {
public void sendSms(String to, UserVo userVo, String message, String messageId, String rateCode) {
reminderSmsMessage[0] = message;
}
});
From here I do something and can track the results of the message send.
bindSmsSender() sinply does:
Method method = SendSMS.class.getDeclaredMethod(”bind”, SendSMS.class);
method.setAccessible(true);
method.invoke(null, sendSMS);
July 8th, 2007 at 4:08 am
[...] Heh, mute the audio and check out my first webcast! This is an experiment, and I don’t show anything groundbraking, in fact I just recode the problem from James Carr blog. I just wanted to show one usefull pattern in action. [...]