Home > Software engineering >  Mock PeriodicTimer for UnitTests
Mock PeriodicTimer for UnitTests

Time:12-23

My class is using PeriodicTimer . I want to mock its time in unit tests. Is it possible? I can set up the class to have a shorter period but that's not the best practice for unit testing.

Nsubstitute example is preferable, but does not really matter.

Maybe it's possible to make a wrapper, however, ValueTask is a bit more tricky. Maybe I need to dig into IValueTaskSource. But maybe someone has a solution?

The code I want to test:

public class Example : BackgroundService
     {
         private readonly PeriodicTimer _timer;
     
         public Example(IConfiguration configuration)
         {
             _timer = new PeriodicTimer(TimeSpan.Parse(configuration["Interval"]));
         }
         
         protected override async Task ExecuteAsync(CancellationToken stoppingToken)
         {
             while (await _timer.WaitForNextTickAsync(stoppingToken)) // I want to simulate this without waiting for real time
             {
                 // Do something
             }
         }
}

CodePudding user response:

The wrapper is the way to go, and it should be a dependency for your class.

Given these types

public interface IPeriodicTimer : IDisposable
{
    ValueTask<bool> WaitForNextTickAsync (CancellationToken cancellationToken = default);
}

public sealed class StandardPeriodicTimer : IPeriodicTimer
{
    private PeriodicTimer _actualTimer;

    public StandardPeriodicTimer(TimeSpan timeSpan)
        => _actualTimer = new PeriodicTimer(timeSpan);

    public async ValueTask<bool> WaitForNextTickAsync(CancellationToken cancellationToken = default)
        => await _actualTimer.WaitForNextTickAsync(cancellationToken);


    public void Dispose() => _actualTimer.Dispose();
}

public class YourDependentClass
{
    public YourDependentClass(IPeriodicTimer timer) => _timer = timer;
    ...
}

You can initialize a YourDependentClass via newing it up, or via dependency injection in production code.

var dependent = new YourDependentClass(new StandardPeriodicTimer(TimeSpan.FromSeconds(1)));

To support the tests, you can create a stub that simply returns true every time without any delay between.

public sealed class TestPeriodicTimer : IPeriodicTimer
{
    public async ValueTask<bool> WaitForNextTickAsync(CancellationToken cancellationToken = default)
        => await Task.FromResult(true);

    public void Dispose()
    {}
}

When you use this stub, we're assuming that somehow the timer stops based on some internal condition in the dependent class.

[Test]
public void TestTheDependentClass()
{
    // arrange
    var timer = new TestPeriodicTimer();
    var dependent = new YourDependentClass(timer);

    // act
    dependent.DoSomething();

    // assert
    // method exits because timer loop is done, timer disposed, etc.
    ...
}

Now, it could get a little trickier if you want the timer to fire a certain number of times, and to run assertions for each tick of the timer. You can either switch from a stub to NSubstitute as you suggest, or carry your stub a step further in its implementation.

But, it gets trickier still. The assertions should be about the state of the dependent after the dependent gets a return value for WaitForNextTickAsync. That means

  • upon the very first call to WaitForNextTickAsync you do nothing
  • on all subsequent calls but the final one you do assertions after the state changes
  • and finally, you have to do assertions when the timer is disposed

I'll make this make sense.

  • Related