.NET Core 中的单元测试托管服务
Unit testing Hosted Services in .NET Core
我有一个由 hosted services 在 .NET Core 中实现的后台任务。这个 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;
}
}
}
我的 Startup
看起来像:
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<IndexingService>();
services.AddTransient<IIndexService, IndexService>();
// 'IndexingSettings' is read from appsetting and registered as singleton
}
如何对 DoWork
方法中的逻辑进行单元测试?问题是托管服务是由框架管理的,我不知道如何隔离这个class。
不确定您所说的隔离 class 是什么意思。这些并不神奇。 ASP.NET 核心只是实例化 class 与任何必需的依赖项,然后调用 StartAsync
,然后在应用程序关闭时调用 StopAsync
。没有什么是您不能自己手动完成的。
换句话说,要对其进行单元测试,您需要模拟依赖项、实例化 class,然后对其调用 StartAsync
。但是,我认为整体托管服务更适合进行集成测试。您可以将任何实际工作分解为一个帮助器 class,这对单元测试来说会更简单,然后简单地 运行 对服务进行集成测试,以确保它通常执行它应该执行的操作。
使用以下命令从依赖项注入容器中检索托管服务:
. . .
services.AddHostedService<IndexingService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
IndexingService indexingService = serviceProvider.GetService<IHostedService>() as IndexingService;
. . .
我有一个由 hosted services 在 .NET Core 中实现的后台任务。这个 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;
}
}
}
我的 Startup
看起来像:
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<IndexingService>();
services.AddTransient<IIndexService, IndexService>();
// 'IndexingSettings' is read from appsetting and registered as singleton
}
如何对 DoWork
方法中的逻辑进行单元测试?问题是托管服务是由框架管理的,我不知道如何隔离这个class。
不确定您所说的隔离 class 是什么意思。这些并不神奇。 ASP.NET 核心只是实例化 class 与任何必需的依赖项,然后调用 StartAsync
,然后在应用程序关闭时调用 StopAsync
。没有什么是您不能自己手动完成的。
换句话说,要对其进行单元测试,您需要模拟依赖项、实例化 class,然后对其调用 StartAsync
。但是,我认为整体托管服务更适合进行集成测试。您可以将任何实际工作分解为一个帮助器 class,这对单元测试来说会更简单,然后简单地 运行 对服务进行集成测试,以确保它通常执行它应该执行的操作。
使用以下命令从依赖项注入容器中检索托管服务:
. . .
services.AddHostedService<IndexingService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
IndexingService indexingService = serviceProvider.GetService<IHostedService>() as IndexingService;
. . .