views:

593

answers:

7

Like it or not, occasionally you have have to write tests for classes that make internal use of timers.

Say for example a class that takes reports of system availability and raises an event if the system has been down for too long

public class SystemAvailabilityMonitor {
 public event Action SystemBecameUnavailable = delegate { };
 public event Action SystemBecameAvailable = delegate { };
 public void SystemUnavailable() {
  //..
 }
 public void SystemAvailable() {
  //..
 }
 public SystemAvailabilityMonitor(TimeSpan bufferBeforeRaisingEvent) {
  //..
 }
}

I have a couple tricks which I use (will post these as an answer) but I wonder what other people do since I'm not fully satisfied with either of my approaches.

A: 

The ways that I usually handle this are either

  1. Set the timer to tick ever 100 milliseconds and figure that its likely my thread will have been switched to by then. This is awkward and produces somewhat indeterministic results.
  2. Wire the timer's elapsed event to a public or protected internal Tick() event. Then from the test set the timer's interval to something very large and trigger the Tick() method manually from the test. This gives you deterministic tests but there's some things that you just can't test with this approach.
George Mauer
A: 

I refactor these such that the temporal value is a parameter to the method, and then create another method that does nothing but pass the correct parameter. That way all the actual behavior is isolated and easily testable on all the wierd edge cases leaving only the very trivial parameter insertion untested.

As an extremely trivial example, if I started with this:

public long timeElapsedSinceJan012000() 
{
   Date now = new Date();
   Date jan2000 = new Date(2000, 1, 1);  // I know...deprecated...bear with me
   long difference = now - jan2000;
   return difference;
}

I would refactor to this, and unit test the second method:

public long timeElapsedSinceJan012000() 
{
   return calcDifference(new Date());
}

public long calcDifference(Date d) {
   Date jan2000 = new Date(2000, 1, 1);
   long difference = d - jan2000;
   return difference;
}
Jared
I'm not sure I get it, how does this involve timers?
George Mauer
+1  A: 

Sounds like one should mock the timer but alas... after a quick Google this other SO question with some answers was the top search hit. But then I caught the notion of the question being about classes using timers internally, doh. Anyhow, when doing game/engine programming - you sometimes pass the timers as reference parameters to the constructors - which would make mocking them possible again I guess? But then again, I'm the coder noob ^^

Oskar Duveborn
No you're right, the ideal way to do it is to pass in a timer object, the only problem is that this breaks some .NET framework conventions and feels awful heavyweight for some things (now I have to configure my IoC container with yet another object for example)
George Mauer
+4  A: 

I extract the timer from the object that reacts to the alarm. In Java you can pass it a ScheduledExecutorService, for example. In unit tests I pass it an implementation that I can control deterministically, such as jMock's DeterministicScheduler.

Nat
Yeah, that some sort of double dispatch (I think?) would be the ideal approach, too bad .NET makes it a hassle to do this. I roll my own timer interface sometimes but always feel like I'm introducing complexity.
George Mauer
Sounds more like dependency injection than double dispatch to me.
Neil Williams
Minor terminology quibble but I think its correct to call this either double dispatch or visitor. Its DI sure but you're also taking an object and telling it to apply its update policy to 'this'.
George Mauer
Fair enough, hadn't thought of it like that... thanks for the enlightenment! :)
Neil Williams
+2  A: 

This is what I am using. I found it in the book: Test Driven - Practical TDD and Acceptance TDD for Java Developers by Lasse Koskela.

public interface TimeSource {
    long millis();
}


public class SystemTime {

    private static TimeSource source = null;

    private static final TimeSource DEFAULTSRC =
        new TimeSource() {
        public long millis() {
            return System.currentTimeMillis();
        }
    };


    private static TimeSource getTimeSource() {
        TimeSource answer;
        if (source == null) {
            answer = DEFAULTSRC;
        } else {
            answer = source;
        }
        return answer;
    }

    public static void setTimeSource(final TimeSource timeSource) {
        SystemTime.source = timeSource;
    }

    public static void reset() {
        setTimeSource(null);
    }

    public static long asMillis() {
        return getTimeSource().millis();
    }

    public static Date asDate() {
        return new Date(asMillis());
    }

}

Notice that the default time source, DEFAULTSRC, is System.currentTimeMillis(). It is replaced in unit tests; however, the normal behavior is the standard system time.

This is where it is used:

public class SimHengstler {

    private long lastTime = 0;

    public SimHengstler() {
        lastTime = SystemTime.asMillis();  //System.currentTimeMillis();
    }
}

And here is the unit test:

import com.company.timing.SystemTime;
import com.company.timing.TimeSource;

public class SimHengstlerTest {
    @After
    public void tearDown() {
        SystemTime.reset();
    }

    @Test
    public final void testComputeAccel() {
        // Setup
        setStartTime();
        SimHengstler instance = new SimHengstler();
        setEndTime(1020L);
    }
    private void setStartTime() {
        final long fakeStartTime = 1000L;
        SystemTime.setTimeSource(new TimeSource() {
            public long millis() {
                return fakeStartTime;
            }
        });
    }
    private void setEndTime(final long t) {
        final long fakeEndTime = t;  // 20 millisecond time difference
        SystemTime.setTimeSource(new TimeSource() {
            public long millis() {
                return fakeEndTime;
            }
        });
    }

In the unit test, I replaced the TimeSource with just a number which was set to 1000 milliseconds. That will serve as the starting time. When calling setEndTime(), I input 1020 milliseconds for the finishing time. This gave me a controlled 20 millisecond time difference.

There is no testing code in the production code, just getting the normal Systemtime.

Make sure to call reset after testing to get back to using the system time method rather than the faked time.

Master1588
A: 

I realize this is a Java question, but it may be of interest to show how its done in the Perl world. You can simply override the core time functions in your tests. :) This may seem horrifying, but it means you don't have to inject a whole lot of extra indirection into your production code just to test it. Test::MockTime is one example. Freezing time in your test makes some things a lot easier. Like those touchy non-atomic time comparison tests where you run something at time X and by the time you check its X+1. There's an example in the code below.

A bit more conventionally, I recently had a PHP class to pull data from an external database. I wanted it to happen at most once every X seconds. To test it I put both the last update time and the update time interval as attributes of the object. I'd originally made them constants, so this change for testing also improved the code. Then the test could fiddle with those values like so:

function testUpdateDelay() {
    $thing = new Thing;

    $this->assertTrue($thing->update,  "update() runs the first time");

    $this->assertFalse($thing->update, "update() won't run immediately after");

    // Simulate being just before the update delay runs out
    $just_before = time() - $thing->update_delay + 2;
    $thing->update_ran_at = $just_before;
    $this->assertFalse($thing->update, "update() won't run just before the update delay runs out");
    $this->assertEqual($thing->update_ran_at, $just_before, "update_ran_at unchanged");

    // Simulate being just after
    $just_after = time() - $thing->update_delay - 2;
    $thing->update_ran_at = $just_after;
    $this->assertTrue($thing->update, "update() will run just after the update delay runs out");

    // assertAboutEqual() checks two numbers are within N of each other.
    // where N here is 1.  This is to avoid a clock tick between the update() and the
    // check
    $this->assertAboutEqual($thing->update_ran_at, time(), 1, "update_ran_at updated");
}
Schwern
+2  A: 

If you are looking for answers to this problem, you might be interested in this blog: http://thorstenlorenz.blogspot.com/2009/07/mocking-timer.html

In it I explain a way to override the usual behavior of a System.Timers.Timer class, to make it fire on Start().

Here is the short version:

class FireOnStartTimer : System.Timers.Timer
{
public new event System.Timers.ElapsedEventHandler Elapsed;

public new void Start()
{
  this.Elapsed.Invoke(this, new EventArgs() as System.Timers.ElapsedEventArgs);
}
}

Of course this requires you to be able to pass the timer into the class under test. If this is not possible, then the design of the class is flawed when it comes to testability since it doesn't support dependency injection. You should change its design if you can. Otherwise you could be out of luck and not be able to test anything about that class which involves its internal timer.

For a more thorough explanation visit the blog.

Thorsten Lorenz