Monday, February 21, 2011

manipulate current time in unit testing?

Is there any way to manipulate the current time in a jUnit 4.5 test? I have the following method which I'd like to have a unit test for

public String getLastWeek() {
  GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("Europe/Stockholm"));
  c.setFirstDayOfWeek(GregorianCalendar.MONDAY);
  c.add(GregorianCalendar.WEEK_OF_YEAR, -1);
  return c.get(GregorianCalendar.YEAR) + " " + c.get(GregorianCalendar.WEEK_OF_YEAR);
}

One way to make it easier to test is to split it into two methods

public String getLastWeek() {
  GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("Europe/Stockholm"));
  return getLastWeekFor(c);
}

public String getLastWeekFor(GregorianCalander c) {
  c.setFirstDayOfWeek(GregorianCalendar.MONDAY);
  c.add(GregorianCalendar.WEEK_OF_YEAR, -1);
  return c.get(GregorianCalendar.YEAR) + " " + c.get(GregorianCalendar.WEEK_OF_YEAR);
}

That lets me test the week subtraction logic, but leaves getLastWeek untested and I prefer to have just one method for this.

From stackoverflow
  • You have no real way of manipulating system time.

    Actually, you refactored the method rather nicely in order to make it testable. I would keep it at that.

  • I can see two paths here.

    Either, create a DateUtils factory class, which encapsulates the construction of Calendar and Date instances. Then you can swap this for a controllable factory. I.e., you have a second implementation of DateUtils that also exposes a setCurrentTime method, and will then explicitly return Calendar instances set to that date.

    Or, you can use JMockit to actually "redefine" your call of new GregorianCalendar so instead a mock instance is returned. I have not done this myself, but there is a description here.

    Your option, breaking the method into two methods, is also a perfectly valid option. I think that it is what most people do - including myself: but the truth is that it does hurt your design, especially if the class is exposed to other code.

    Yuval A : The first two suggestions, while are perfectly valid and even heavily used in other situations, feel a bit of an overkill to OP's needs.
  • Looks like you're trying to recreate what JODA Time does. I'll cite it in case you aren't aware. You can make the trade-off between reinventing the wheel and adding a dependency.

    NA : Actully I found JODA earlier today and realised that was what I actully wanted to be using. How would a testable JODA version look? Or should I simply assume that JODA is working correct as it should have it's own tests?
    duffymo : Check the JODA source out and see if it's unit tested. I'm betting that it is. Usually I assume a 3rd party library is tested until it gives me a reason to believe otherwise. At worst, they have a bigger user base than you do. Lots of people banging on code is a good way to find and fix lots of errors.

0 comments:

Post a Comment