views:

94

answers:

2

I have to test a method which does a certain amount of work after an interval.

while (running)
{
    ...
    // Work
    ...
    Thread.Sleep(Interval);
}

Interval is passed in as a parameter to the class so I can just pass in 0 or 1 but I was interested as to how to mock the system clock if this wasn't the case.

In my test I'd like to be able to simply set the time forward by TimeSpan Interval and have the thread wake up.

I've never written tests for code which acts upon the executing thread before and I'm sure there are some pitfalls to avoid - please feel free to elaborate on what approach you use.

Thanks!

+7  A: 

If you do not wish to test the fact that the thread actually sleeps, a more straightforward approach (and one that is possible) is to have an ISleepService. You can then mock this out, and then not sleep in your tests, but have an implementation that does cause a Thread.Sleep in your production code.

ISleepService sleepService = Container.Resolve<ISleepService>();

..

while (running)
{
    ...
    // Work
    ...
    sleepService.Sleep(Interval);
}

Example using Moq:

    public interface ISleepService
    {
        void Sleep(int interval);
    }

    [Test]
    public void Test()
    {
        const int Interval = 1000;

        Mock<ISleepService> sleepService = new Mock<ISleepService>();
        sleepService.Setup(s => s.Sleep(It.IsAny<int>()));
        _container.RegisterInstance(sleepService.Object);

        SomeClass someClass = _container.Resolve<SomeClass>();
        someClass.DoSomething(interval: Interval);

        //Do some asserting.

        //Optionally assert that sleep service was called
        sleepService.Verify(s => s.Sleep(Interval));
    }

    private class SomeClass
    {
        private readonly ISleepService _sleepService;

        public SomeClass(IUnityContainer container)
        {
            _sleepService = container.Resolve<ISleepService>();
        }

        public void DoSomething(int interval)
        {
            while (true)
            {
                _sleepService.Sleep(interval);
                break;
            }
        }
    }

Update

On a design\maintenance note, if it is painful to change the constructor of "SomeClass", or to add Dependency Injection points to the user of the class, then a service locator type pattern can help out here, e.g.:

private class SomeClass
{
    private readonly ISleepService _sleepService;

    public SomeClass()
    {
        _sleepService = ServiceLocator.Container.Resolve<ISleepService>();
    }

    public void DoSomething(int interval)
    {
        while (true)
        {
            _sleepService.Sleep(interval);
            break;
        }
    }
}
chibacity
Fantastic - this makes a lot of sense - Moq was definitely on the road map too so you answer has provided an easy initiation.
gav
+1  A: 

You can't really mock the system clock.

If you need to be able to alter the suspend behavior of code like this, you will need to refactor it so that you are not calling Thread.Sleep() directly.

I would create a singleton service, which could be injected into the application when it's under test. The singleton service would have to include methods to allow some external caller (like a unit test) to be able to cancel a sleep operation.

Alternatively, you could use a Mutex or WaitHandle object's WaitOne() method which has a timeout parameter. This way you could trigger the mutex to cancel the "sleep" or let it timeout:

public WaitHandle CancellableSleep = new WaitHandle(); // publicly available

// in your code under test use this instead of Thread.Sleep()...
while( running ) {
    // .. work ..
    CancellableSleep.WaitOne( Interval ); // suspends thread for Interval timeout
}


// external code can cancel the sleep by doing:
CancellableSleep.Set(); // trigger the handle...
LBushkin
A WaitHandle is a very heavy way (in terms of system resources and performance) to provide an alternative to `Thread.Sleep()`. I would not recommend a WaitHandle unless you have other code that can provide guidance as to when the thread should wake up.
Dan Bryant
+1 for not being able to mock the system clock.
Larsenal
Well actually you could use system call interception, but that would definitely be overkill.
chibacity
@Dan Bryant: I don't dispute that as a general practice, a wait handle is more expensive than `Thread.Sleep()`. However, in a single constrainted scenario it can make sense - especially if you use an injectable service (as I mention) to only do so when testing. For release code, one would presumably use the more lightweight `Sleep()` call.
LBushkin
@chibacity: While you could use system call intercepts, that would effect all code that depends on the system clock - which could adversely affect other code that is not being tested.
LBushkin
@LBushkin I totally agree. Any sort of wall-clock time mangling can have unintended consequences. Best to stick to logical time and rely on event ordering. :)
chibacity