Testing Time Dependent Code

The Problem

Writing unit tests is a great way to ensure your code works and ensure that other programmers don’t break your code by accident. Not only that the whole testing philosophy seems to encourage you to think about making your code as modular as possible in order to be make it testable, thus making it more robust architecturally.

However, difficulties often seem to arise with when testing pieces of code that are strongly associated to any sort of I/O with external systems, or anything else that is tied to some sort of global side effect, such systems need to use randomisation or has time dependencies. Over the years most test frameworks have managed to over come these issues through the use of Mock Objects.

One issue though seems to haunt me a lot is dealing with systems which trigger events based on the system time. For instance credit based billing systems where the amount you pay for a service is based on the amount of time you actively use it. A good example would be property website where users will get billed a fixed amount for each active property that have listed on a monthly basis. In a test scenario we need to be able create a property, and simulate going into future by a set period of time and ensure that our system is able to pick up and bill our user for the time period we’ve moved forward by.

A Possible Solution

Thankfully, Ruby’s dynamic ability to override core features allows us to rewrite how the Time class behaves in a test environment. This can easily be done by making the Time.now method return a different time if we have decided to manually override the time. A piece of sample code you could use for in your Test/Spec helpers might be:

Source Listing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Time
  # Holds the fake times
  @@active = nil
  cattr_accessor :active

  class << self
    # Return fake time if the time has been overriden
    def now
      active || new
    end
    
    # Set the time to be fake for a given block of code
    def is(new_time, &code)
      old_time = active
      self.active = new_time
      begin
        code.call if code
      ensure
        self.active = old_time
      end
    end
  end
end

How It Works

This code should be fairly simple to understand for those who know Ruby. Basically we use a class variable to hold the overridden time value. If this is not set it will get it from the system clock via the Time.new method which does the same thing as the old Time.now method. While we can override the time using the class writer for Time.active=, it is preferable that we use Time.is as this protects us from possible control flow issues that arise from not resetting the time back if pre maturely exit our code, either by accident through programmer error (i.e. returning from a function), or because of an Exception / failed test case. The use of blocks + ensure handles this in a similar way to RAII in C++.

Example Usage

As stated above, we should protect ourselves by using the Time.is method in critical areas, or at least use Time.active = nil in the tear down process of our test, otherwise we run the risk of affecting other our test cases incorrectly.

Below shows a basic pseudo code example for the property scenario I outlined earlier:

1
2
3
4
5
6
7
property = Property.create # Put our attributes here
property.months_billed.should == 1
Time.is 3.months.since do
  property.bill!
  Time.active = 1.hour.since
  property.months_billed.should == 4
end

Listening to Rock:
Be Quiet And Drive (Far Away) - Deftones

Free Designs

Below are a handful of open source / free web designs