I have a background task implemented by hosted services in .NET Core. There is very little logic in this class:
public class IndexingService : IHostedService, IDisposable
{
private readonly int indexingFrequency;
private readonly IIndexService indexService;
private readonly ILogger logger;
private bool isRunning;
private Timer timer;
public IndexingService(ILogger<IndexingService> logger, IIndexService indexService, IndexingSettings indexingSettings)
{
this.logger = logger;
this.indexService = indexService;
this.indexingFrequency = indexingSettings.IndexingFrequency;
}
public void Dispose()
{
this.timer?.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.timer = new Timer(this.DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(this.indexingFrequency));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
private void DoWork(object state)
{
if (this.isRunning)
{
// Log
return;
}
try
{
this.isRunning = true;
this.indexService.IndexAll();
}
catch (Exception e)
{
// Log, The background task should never throw.
}
finally
{
this.isRunning = false;
}
}
}
and my Startup
looks like:
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<IndexingService>();
services.AddTransient<IIndexService, IndexService>();
// 'IndexingSettings' is read from appsetting and registered as singleton
}
How can I unit test the logic in DoWork
method? The problem is that the hosted services are managed by the framework, and I don't know how to isolate this class.
Not sure what you mean about isolating the class. These aren't magical. ASP.NET Core just instantiates the class with any required dependencies, and then calls StartAsync
, and later StopAsync
on app shutdown. There's nothing of this that you cannot do yourself manually.
In other words, to unit test it, you'd mock the dependencies, instantiate the class, and call StartAsync
on it. However, I think overall hosted services are a better candidate for integration testing. You can factor out any real work into a helper class that would be more simplistic to unit test, and then simply run an integration test on the service to ensure that it generally does what it's supposed to do.