Nov 07 2009

No more excuses 2: junit testing System time

Category: java,tutorialgiordano scalzo @ 2:59 pm

Another common exemple of static dependency, it’s the Time System dependency.
In greenfield project it could be possible inject time into the objects they’ll use it, but in legacy code it’s more difficult, so oftet we gave up and don’t unit test some behaviour dependent from time.

In a recent project, I had the problem to check the correct format of a filename that it had to contain the creation timestamp; I easily overcame it with a nice test pattern made by Paolo “Nusco” Perrotta, well known Ruby Metaprogramming guru, in a version wrote by Bruno Bossola in a nice Open Source project.

In a nutshell, the trick is to adapt System Time with another object.

Let see a simple example.
This is the class to test:

package biz.scalzo.prod;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeFormatter {
	public String now() {
		SimpleDateFormat sdf = new SimpleDateFormat(
				"yyyy/MM/dd HH:mm");
		return sdf.format(new Date(System.currentTimeMillis()));
	}
}

I know it mixes two responsibilities, it has a silly static dependecy and so on, but forgive my oversemplification used to show the pattern.

package biz.scalzo.test;

import junit.framework.Assert;
import org.junit.Test;

public class ATimeFormatter {
	@Test
	public void shouldFormatNowInFancyWay() {
		Assert.assertEquals("2009/10/13 15:29", new TimeFormatter().now());
	}

}

The behaviour to check is how well the Formatter formats current time in string; we have to find a way to set a well know time point so we can check te result of format.

To implement the VirtualClock pattern, we declare a simple Clock interface:

package biz.scalzo.prod;

public interface Clock {
	public long now();
}

and a repository where we can insert our fake clock

package biz.scalzo.prod;

import java.util.Date;

public class SystemTime {
	private static Clock clock;

	private static final Clock defaultClock = new Clock() {
		public long now() {
			return System.currentTimeMillis();
		}
	};

	public static long asMillis() {
		return getClock().now();
	}

	public static Date asDate() {
		return new Date(getClock().now());
	}

	public static void reset() {
		clock = null;
	}

	public static void setClock(Clock aClock) {
		clock = aClock;
	}

	static Clock getClock() {
		return (clock != null ? clock : defaultClock);
	}
}

Now we can change our class using SystemTime instead of System.currentTimeMillis():

package biz.scalzo.prod;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeFormatter {
	public String now() {
		SimpleDateFormat sdf = new SimpleDateFormat(
				"yyyy/MM/dd HH:mm");
		return sdf.format(SystemTime.asDate());
	}
}

In test harness we can inject a fake clock

package biz.scalzo.test;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import biz.scalzo.prod.Clock;
import biz.scalzo.prod.SystemTime;
import biz.scalzo.prod.TimeFormatter;

public class ATimeFormatter {
	private static final long FAKED_TIME = 1255440571354L;

	private void fakeClock() {
		SystemTime.setClock(new Clock() {
			public long now() {
				return FAKED_TIME;
			}
		});
	}

	@Before
	public void setUp(){
		fakeClock();
	}

	@Test
	public void shouldFormatNowInFancyWay() {
		Assert.assertEquals("2009/10/13 15:29", new TimeFormatter().now());
	}

}

and we have green bar! Neat, isn’t it?

I know it’s a lot of code, but in legacy code it’s better than leave a feature without a test.

Technorati Tags: , ,

Tags: , ,

  • http://emptylist.wordpress.com Andrea Cerisara

    Great, please go on with this series! :-) The same pattern is described also in Koskela’s Test Driven book.

  • http://giordano.scalzo.biz giordano scalzo

    I knew Koskela’s book is a must read, but my pile is very tall.
    I’ll add it with this http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627

  • http://emptylist.wordpress.com Andrea Cerisara

    Me too, I’ve just booked it :-)