Is it possible to set the time in unit test?

Following from this question ... I am trying to execute unit test the following script:

I have a class that allows you to call a method to perform an action, and if it fails, wait a second and remember this method.

Suppose I wanted to call the DoSomething () method ... but in case of exception from DoSomething (), I want to retry, calling it up to a maximum of 3 times, but wait 1 second between each attempt. The purpose of the unit test, in this case, is to verify that when we called DoSomething () 3 times with 1 second of waiting between each attempt, the total time takes> = 3 seconds.

Unfortunately, the only way I can test it is when it uses a stopwatch .... which has two side effects ...

  • it takes 3 seconds to complete the test ... and I usually like my tests to run in milliseconds.
  • the amount of time to start the test varies by +/- 10 ms or so, which can lead to a test failure if this variance is not taken into account.

What would be nice if there was a way to exhaust this dependence in time so that my test could work faster and be sufficiently consistent in it. Unfortunately, I can’t figure out how to do this ... and so I thought I would ask and see if some of you ever had this problem ...

+4
source share
5 answers

You can create a Waiter class that provides a method, Wait(int) , that waits for a specified amount of time. Make tests for this method, then in unit test, go to the mocked version, which simply keeps track of how long it is asked to wait, but returns immediately.

For example (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; } } 
+7
source

The trick is to create a time provider mockup.

In Ruby, you simply use the mock library:

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

In Java, this is a little trickier. Replace the usual time with your "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, Clock can be entered in other ways, but now we have a testable and extensible method.

+4
source

I often test retry attempts like this. The method I use is to make the timeout customizable - then I can set it to zero in my tests. In your case, you can mock the object on which DoSomething () is called, wait for 3 calls and set the timeout to 0 seconds - then you can check that DoSomething () is called 3 times in a row.

Another way to do this would be to have an ITimer interface, on which you call Wait (int seconds) between each DoSomething () call, then you could exhaust ITimer and verify that Wait (int) is called 3 times with the correct argument for the number of seconds. The specific ITimer implementation then executes Thread.sleep or any other method that you use to wait.

+2
source

I wrote a series of blog posts about testing the timer class in C ++. As others have said, the key is to separate the time provider and then mock him. Once you do, everything will be easy. See here: http://www.lenholgate.com/blog/2004/05/practical-testing.html if you are interested.

+2
source

Your instincts are true. The trick is to separate the different parts of the problem, rather than combining them into one class. You need an Action object that implements DoSomething, which is time-independent; a Retryer object that will control attempts to call an Action object; and watches that are waiting. You can unit test the Action object directly, unit test Retryer with pointed Clock and Action objects and make Clock so simple that it just works.

First of all, do not put real dreams in tests, it is too fragile and slow.

+1
source

All Articles