Time Dependent Unit Tests

Time dependent unit tests

Joda time supports setting a "fake" current time through the setCurrentMillisFixed and setCurrentMillisOffset methods of the DateTimeUtils class.

See https://www.joda.org/joda-time/apidocs/org/joda/time/DateTimeUtils.html

UnitTest a method that is time-dependent

Two main options:

  1. Create a service that isolates time-dependent functionality and inject its interface into classes that need it. Then you can mock this interface to get the behavior you need for testing.

  2. Use a detouring test framework like Moles (there are a few others out there, as well) that can reroute static method calls like DateTime.Now.

How to unit test a time dependent method with JUnit

I would build a unix timestamp in my unitTest based on the current time. The granularity of the result of the method is coarse enough that a few millisecond difference between when you create the argument and when this method is executed will not be material. Granted, you will need to steer clear of the boundary conditions, i.e., a unixTime at 60 minutes and 24 hours.

public void testMinutes() {
Instant minutesAgo = Instant.ofEpochMilli(System.currentTimeMillis() - 15 * 60 * 1000);
String result = getAge(minutesAgo.getEpochSecond());
assertSomething...
}

And so on for hours and days tests.

Time dependent unit tests breaking when run in parallel, any workarounds?

Implement and set a CurrentMillisProvider that stores "now" in a ThreadLocal<Long>, as suggested in these Parallelism Tips:

P.S. Joda Time has DateTimeUtils which lets you change the current time used by the library. It's a global variable, but if you call DateTimeUtils.setCurrentMillisProvider with a ThreadLocal backed implementation, it'll be reasonably isolated when testing legacy code that uses Joda Time.

How to write time dependent tests Python

freezegun makes mocking datetime very easy.

from freezegun import freeze_time

@freeze_time("2015-02-25")
def test_third_day_from_now():
assert third_day_from_now() == datetime.datetime(2015, 2, 28)

Testing a time-dependent method

In general, I think it's a good idea to make your nontrivial functions and classes as close to mathematical functions (e.g., sin(x)), as possible. Given the same input, a mathematical function gives the same output each time, irrespective of the current date, random choices, and so forth.

  • If your function performs nontrivial logic dependent on the current date or time, pass the current date or time externally to it.

  • If your function performs random choices, pass it a pseudo-random number generator.

So, for example, instead of:

import datetime

def foo():
...
now = datetime.datetime.now()
...

foo()

Use

import datetime

def foo(now):
...
...

foo(datetime.datetime.now())

This makes your nontrivial code consistent across multiple executions.

  1. You can predictably test it.

  2. If it fails in production, it is easier to reconstruct the problem.

Unit testing code dependent on DateTime.Now

Kata is dependent on an implementation concretion when it should be dependent on an abstraction. That would allow Kata the flexibility at accept different implementations of the abstraction.

public class Kata {
private readonly IClock clock;

public Kata(IClock clock) {
this.clock = clock;
}

public int GetAgeFromDOB(DateTime birthday) {
var now = clock.Now;
var age = now.Year - birthday.Year;
if (birthday > now.AddYears(-age)) age--;
return age;
}
}

public interface IClock {
DateTime Now { get; }
}

The two implementations of IClock can be kept simple based on the requirements stated in the question.

The SystemClock class needs to implement a DateTime method that returns DateTime.Now

public class SystemClock : IClock {
public DateTime Now { get { return DateTime.Now; } }
}

The StaticClock method needs to implement a DateTime method that returns a DateTime value that was passed to its constructor.

public class StaticClock : IClock {
public StaticClock(DateTime now) {
this.Now = now;
}

public DateTime Now { get; private set; }
}

Which should allow you to get the desired results.



Related Topics



Leave a reply



Submit