Archive for the ‘date’ tag
Testing with Joda Time
The alternative to dealing with java.util.Date which I wrote about in a previous post is to make use of the Joda Time library. I'm led to believe that a lot of the ideas from Joda Time will in fact be in Java 7.
Nevertheless when testing with Joda Time there are times when it would be useful for us to have control over the time our code is using.
Why would we want to control the time?
There are a couple of situations that come to mind where it may be useful to be able to control the time in a system:
- There is a piece of code which only executes at a certain time of the day. To see if it executes correctly we need to be able to set the system time to be that time.
- Date calculations – we want to do a calculation on a date and verify the result. We therefore need to be able to control the original date.
Given that, there are two approaches which I have seen to allow us to do this:
Freezing time
Joda includes a DateTimeUtils class which allows us to change the current time.
On the projects I've worked on we would typically wrap these calls in a more descriptive class. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; public class JodaDateTime { public static void freeze(DateTime frozenDateTime) { DateTimeUtils.setCurrentMillisFixed(frozenDateTime.getMillis()); } public static void unfreeze() { DateTimeUtils.setCurrentMillisSystem(); } } |
This approach works better if DateTime is deeply engrained in the system and it is difficult for us to abstract dates behind another interface.
The benefit of taking this approach is that we can test for dates without having to change any of our code to add in another level of abstraction which leads to further complexity.
Time Provider
The alternative approach is to have a TimeProvider which we can pass around the system. This would typically be passed into the constructor of any classes which need to make use of time.
For example, we might have the following interface defined:
1 2 3 4 5 | import org.joda.time.DateTime; public interface TimeProvider { public DateTime getCurrentDateTime() ; } |
We can then mock out getCurrentDate() to return whatever date we want in our tests.
The advantage of this approach is that it allows more flexibility around the implementation – it could be used to sync system and local machine dates for example – although at a cost of adding extra complexity.
This approach is similar to the plugin pattern Martin Fowler details in Patterns of Enterprise Application Architecture in that we use one implementation of TimeProvider in our application and then a different version for testing.
I generally favour this approach if possible although if a quick win is needed then the first approach is fine.
Using java.util.Date safely
Assuming that you are unable to use Joda Time on your project, there are some simple ways that I have come across that allow you to not suffer at the hands of java.util.Date.
What's wrong with java.util.date in the first place?
First of all java.util.date is mutable. This means that if you create a java.util.date object its state can be modified after creation.
This means that you can do an operation like the following, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import java.util.Date; import java.util.Calendar; public class DateTest { public static void main(String[] args) { Date aDate = createDate(1, 0, 2008); System.out.println(aDate); aDate.setTime(createDate(1, 0, 2009).getTime()); System.out.println(aDate); } private static Date createDate(int date, int month, int year) { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DATE, date); calendar.set(Calendar.MONTH, month); calendar.set(Calendar.YEAR, year); return new Date(calendar.getTimeInMillis()); } } |
Ignoring the horridness of the zero based month on Calendar, the output of the above piece of code (when I ran it) is as follows.
Tue Jan 01 23:41:50 GMT 2008 Thu Jan 01 23:41:50 GMT 2009
The 'aDate' object has actually had its value changed by this piece of code. Clearly this means that we have to be careful how we handle uses of java.util.Date in our code to ensure unexpected things don't happen.
The problems java.util.Date can cause
Often when looking at code we will notice dates being returned via a getter from a class:
1 2 3 4 5 6 7 | public class DateTest { private Date aDate; public Date getADate() { return aDate; } } |
Eventually we would like to get to a stage where aDate is encapsulated inside the DateTest class but for now we just want to ensure that clients of DateTest can't change the value of the 'aDate' field in DateTest. Right now this is what will happen if a client changed the value returned by getADate() because 'aDate' is a reference type.
If we want to return 'aDate' we need to ensure that the value in DateTest cannot be changed by clients of this class. We can do this by returning a copy of the value:
1 2 3 4 5 6 7 | public class DateTest { private Date aDate; public Date getADate() { return new Date(aDate.getTime()); } } |
We have the same problem when setting dates – the reference which you set it to will still be changeable from outside the class.
e.g.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import java.util.Date; import java.util.Calendar; public class DateTest { private Date aDate; public void setADate(Date aDate) { this.aDate = aDate; } public static void main(String[] args) { Date myDate = createDate(1,0,2008); new DateTest().setADate(myDate); } private static Date createDate(int date, int month, int year) { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DATE, date); calendar.set(Calendar.MONTH, month); calendar.set(Calendar.YEAR, year); return new Date(calendar.getTimeInMillis()); } } |
If we change myDate in this scenario the 'aDate' field in the DateTest object will also be changed. We can get around this the same way as before by creating a new date with the value passed in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import java.util.Date; public class DateTest { private Date aDate; public void setADate(Date aDate) { this.aDate = new Date(aDate.getTime()); } public static void main(String[] args) { Date myDate = someDate(); new DateTest().setADate(myDate) } } |
Joshua Bloch and Neal Gafter's Java Puzzlers has more on this topic and other interesting quirks in Java.