[Unit Testing]: Time zone and stuff…
Do you know those nasty time zone related bugs, too?
They are quite common – but often they are discovered when it is too late. Often every developer and tester lives in the same time zone. So sometimes the software is never ever run in a different time zone until it is shipped…
The problem
The idea of time zones makes calculating with dates/times really difficult.
But probably we have to live with them for a few more years…
So as developers we have to handle them properly.
And handling them properly means two things
Using a nice API
I am certain that it is impossible to write bug free code using just the standard Calendar stuff.
Maybe James Gosling is able to do that – but I even doubt that…
So just use one of those APIs that work. My personal favorite is Joda Time. Many cases then just work.
Testing time zones
As soon as your software handles dates/times it is necessary to test that code.
But my experience shows that hardly anybody tests anything that needs a lot of work to test. And changing the time zone manually needs some (too much?) work.
The solution
JUnit offers rules. Very simple but powerful feature. I have created several custom rules that make live much easier…
And I have created a timezone rule (based on Joda Time) that allows me to run my unit tests in different time zones.
Here it comes:
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.joda.time.DateTimeZone;
import org.junit.rules.*;
import org.junit.runners.model.*;
/**
* Rule that sets the TimeZone
*
* @author Johannes Schneider (js@cedarsoft.com)
*/
public class DateTimeZoneRule implements MethodRule {
@NotNull
protected final DateTimeZone zone;
public DateTimeZoneRule() throws IllegalArgumentException {
this( "America/New_York" );
}
public DateTimeZoneRule( @NotNull @NonNls String zoneId ) throws IllegalArgumentException {
this( DateTimeZone.forID( zoneId ) );
}
public DateTimeZoneRule( @NotNull DateTimeZone zone ) {
this.zone = zone;
}
private DateTimeZone oldTimeZone;
@Override
public Statement apply( final Statement base, FrameworkMethod method, Object target ) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
} finally {
after();
}
}
};
}
private void before() {
oldTimeZone = DateTimeZone.getDefault();
DateTimeZone.setDefault( zone );
}
private void after() {
DateTimeZone.setDefault( oldTimeZone );
}
@NotNull
public DateTimeZone getZone() {
return zone;
}
@NotNull
public DateTimeZone getOldTimeZone() {
if ( oldTimeZone == null ) {
throw new IllegalStateException( "No old zone set" );
}
return oldTimeZone;
}
}
No magic, nothing special, no rocket science.
Just start using rules – if will change the world into a better place…