views:

266

answers:

5

Following on from this question...I'm trying to unit test the following scenario:

I have a class that allows one to call a method to perform some action and if it fails wait a second and recall that method.

Say I wanted to call a method DoSomething()...but in the event of an exception being thrown by DoSomething() I want to be able to retry calling it up to a maximum of 3 times but wait 1 second between each attempt. The aim of the unit test, in this case, is to verify that when we called DoSomething() 3 times with 1 second waits between each retry that the total time taken is >= 3 seconds.

Unfortunately, the only way I can think to test it, is to time it using a Stopwatch....which has two side effects...

  1. it takes 3 seconds to execute the test...and I usually like my tests to run in milliseconds
  2. the amount of time to run the test varies by +/- 10ms or so which can cause the test to fail unless I take this variance into account.

What would be nice is if there were a way to mock out this dependency on time so that my test is able to run quicker and be fairly consistent in it's results. Unfortunately, I can't think of a way to do so...and so I thought I'd ask and see if any of you out there have ever encountered this problem...

+2  A: 

I often test retry attempts like this. The method I use is to make the timeout configureable - then I can set it to zero in my tests. In your case you could mock the object DoSomething() is called on, expect 3 calls to it and set the timeout to 0 seconds - then you can verify that DoSomething() is called 3 times straight away.

The other way to do it would be to have an interface ITimer on which you call Wait(int seconds) between each call to DoSomething() - then you could mock out ITimer and verify Wait(int) is called 3 times with the correct argument of number of seconds. The concrete implementation of ITimer then does a Thread.sleep or whatever method you use for doing the waiting.

codingfloor
+6  A: 

You could make a Waiter class who provides a method, Wait(int) that waits the amount of time specified. Make tests for this method, then in your unit test, pass in a mocked up version that simply keeps track of how long it was asked to wait, but returns immediately.

For instance (C#):

interface IWaiter
{
    void Wait(int milliseconds);
}

class Waiter : IWaiter
{
    public void Wait(int milliseconds)
    {
        // not accurate, but for the sake of simplicity:
        Thread.Sleep(milliseconds);
    }
}

class MockedWaiter : IWaiter
{
    public void Wait(int milliseconds)
    {
        WaitedTime += milliseconds;
    }
    public int WaitedTime { get; private set; }
}
Matthew Scharley
+3  A: 

The trick is to create a mock of the time provider.

In Ruby, you simply use the mocking library:

now = Time.now Time.expects(:now).returns(now).once Time.expects(:now).returns(now + 1).once etc.

In Java, it's a bit more complicated. Replace regular time with your own "Clock"

interface Clock {
  Date getNow();
}

and then, rewrite:

public String elapsedTime(Date d) {
  final Date now = new Date();
  final long ms = now.getTime() - d.getTime();
  return "" + ms / 1000 + " seconds ago";
}

as:

public String elapsedTime(Date d, Clock clock) {
  final Date now = clock.getNow();
  final long ms = now.getTime() - d.getTime();
  return "" + ms / 1000 + " seconds ago";
}

Obviously the Clock can be injected in other ways, but now we have a testable and extensible method.

ndp
In C# 3.0, you can replace the Clock interface with a Func<DateTime>.
Mark Seemann
There are mocking libraries for Java. EasyMock for example.
Wim Coenen
For Java, code usually fetches the current time from new Date(). Using EasyMock doesn't help, as you have no way to override the date returned. Hence the introduction of the wrapper "Clock" class. Sorry I didn't explain this well.
ndp
+1  A: 

Your instincts are correct. The trick is to separate out the different parts of the problem, rather than bundling them into a single class. You want an Action object that implements DoSomething that is independent of timing; a Retryer object that will manage the attempts to call the Action object; and a Clock that waits. You can unit test the Action object directly, unit test the Retryer with stubbed Clock and Action objects, and make the Clock so simple that it just works.

Above all, don't put real sleeps in the tests, it's just too brittle and slow.

Steve Freeman
+2  A: 

I wrote a whole series of blog posts on testing a timer class in C++. As others have said, the key is to separate out the time provider and then mock it out. Once you've done that the rest is easy. See here: http://www.lenholgate.com/archives/000306.html if you're interested.

Len Holgate