Reputation: 1150
In .NET Core, background tasks are implemented as IHostedService. This is my hosted service:
public interface IMyService {
void DoStuff();
}
public class MyHostedService : IHostedService, IDisposable
{
private const int frequency;
private readonly IMyService myService;
private Timer timer;
public MyHostedService(IMyService myService, Setting s)
{
this.myService = myService;
frequency = s.Frequency;
}
public void Dispose()
{
this.timer?.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.timer = new Timer(this.DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(this.frequency));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
private void DoWork(object state)
{
try
{
this.myService.DoStuff();
}
catch (Exception e)
{
// log
}
}
}
I am trying to unit test this class, and all I want is to make sure DoStuff
gets called when StartAsync
method is called. This is my unit test:
[TestFixture]
public class MyHostedServiceTests
{
[SetUp]
public void SetUp()
{
this.myService = new Mock<IMyService>();
this.hostedService = new MyHostedService(this.myService.Object, new Setting { Frequency = 60 });
}
private Mock<ImyService> myService;
private MyHostedService hostedService;
[Test]
public void StartAsync_Success()
{
this.hostedService.StartAsync(CancellationToken.None);
this.myService.Verify(x => x.DoStuff(), Times.Once);
}
}
Why is this failing?
Upvotes: 1
Views: 1192
Reputation: 247018
It is failing because the async code is executing on a separate thread to the code that is verifying the expected behavior. That and the fact the the verifying code in invoked before the timer has had time to be invoked.
When testing an async method the test in most cases should also be async.
In this case you also need to let some time pass to allow the timer to invoke.
use Task.Delay
to give the timer enough time to perform its function.
For example
[TestFixture]
public class MyHostedServiceTests {
[SetUp]
public void SetUp() {
this.myService = new Mock<IMyService>();
this.setting = new Setting { Frequency = 2 };
this.hostedService = new MyHostedService(this.myService.Object, setting);
}
private Mock<ImyService> myService;
private MyHostedService hostedService;
private Setting setting;
[Test]
public async Task StartAsync_Success() {
//Act
await this.hostedService.StartAsync(CancellationToken.None);
await Task.Delay(TimeSpan.FromSeconds(1));
await this.hostedService.StopAsync(CancellationToken.None);
//Assert
this.myService.Verify(x => x.DoStuff(), Times.Once);
}
}
Above example uses a shorter frequency to test the expected behavior
Upvotes: 1