.NET Core 在集成测试中停止 HostedService
.NET Core stop HostedService in the Integration test
我有 .NET Core web API 项目,由于某些原因,我们在这个项目中创建了一个后台服务,并在应用程序启动时启动 运行ning 后台服务。
因此,我们创建了一个 BackgroundWorkderService
,它继承自 BackgroundService (Microsoft.Extensions.Hosting),如下所示:
public class BackgroundWorkerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await DoWork(stoppingToken);
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
await ExecuteAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
为了 运行 它在应用程序启动时,我将后台服务添加到 Program.cs 中的托管服务,如下所示:
.ConfigureServices(services =>
services.AddHostedService<BackgroundWorkerService>());
现在,我们需要创建一个集成测试,我们想在 运行 集成测试时停止后台服务。
有谁知道如何在集成测试中停止它?我试图从 ConfigureTestServices 中删除该服务,但没有成功,当集成测试开始时,后台服务仍然 运行s。
我找到了一个解决方案,可以将后台服务寄存器设置为如下状态。
在注册后台服务部分编辑 Program.cs 文件:
.ConfigureServices(services =>
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "INTEGRATION")
{
services.AddHostedService<BackgroundWorkerService>();
}
});
然后尝试根据需要将变量更改为 INTEGRATION。
正如您在问题中提到的那样,您应该能够删除 ConfigureTestServices
中的服务。然而,我们究竟如何指定要删除的服务是这里的关键。
根据我的经验,您需要 找到 有问题的 ServiceDescriptor
而不是创建一个具有预期值的新值(请参阅下面的注释了解原因)。我使用 LINQ 和服务的 实现 类型(在您的例子中,BackgroundWorkerService
)轻松找到现有的 ServiceDescriptor
。然后我可以将它从列表中删除,这意味着它不会在 WebApplicationFactory
.
上调用 CreateClient()
时启动
看起来像这样:
builder.ConfigureTestServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundWorkerService));
services.Remove(descriptor);
}
这种方法的一个非常好的好处是它将测试逻辑排除在生产代码之外,并且完全在 setup/fixture 中用于测试。
关于 IServiceCollection
删除的注意事项:经过一番探究,我相信这是因为 ServiceDescriptor
doesn't provide an equality or comparison method other than Object.Equals
,这又回到了引用相等性。因此,即使新 ServiceDescriptor
中的所有值都相同,它们也会是不同的对象,因此不会被发现并从服务列表中删除。
我有 .NET Core web API 项目,由于某些原因,我们在这个项目中创建了一个后台服务,并在应用程序启动时启动 运行ning 后台服务。
因此,我们创建了一个 BackgroundWorkderService
,它继承自 BackgroundService (Microsoft.Extensions.Hosting),如下所示:
public class BackgroundWorkerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await DoWork(stoppingToken);
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
await ExecuteAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
为了 运行 它在应用程序启动时,我将后台服务添加到 Program.cs 中的托管服务,如下所示:
.ConfigureServices(services =>
services.AddHostedService<BackgroundWorkerService>());
现在,我们需要创建一个集成测试,我们想在 运行 集成测试时停止后台服务。
有谁知道如何在集成测试中停止它?我试图从 ConfigureTestServices 中删除该服务,但没有成功,当集成测试开始时,后台服务仍然 运行s。
我找到了一个解决方案,可以将后台服务寄存器设置为如下状态。
在注册后台服务部分编辑 Program.cs 文件:
.ConfigureServices(services =>
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "INTEGRATION")
{
services.AddHostedService<BackgroundWorkerService>();
}
});
然后尝试根据需要将变量更改为 INTEGRATION。
正如您在问题中提到的那样,您应该能够删除 ConfigureTestServices
中的服务。然而,我们究竟如何指定要删除的服务是这里的关键。
根据我的经验,您需要 找到 有问题的 ServiceDescriptor
而不是创建一个具有预期值的新值(请参阅下面的注释了解原因)。我使用 LINQ 和服务的 实现 类型(在您的例子中,BackgroundWorkerService
)轻松找到现有的 ServiceDescriptor
。然后我可以将它从列表中删除,这意味着它不会在 WebApplicationFactory
.
CreateClient()
时启动
看起来像这样:
builder.ConfigureTestServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundWorkerService));
services.Remove(descriptor);
}
这种方法的一个非常好的好处是它将测试逻辑排除在生产代码之外,并且完全在 setup/fixture 中用于测试。
关于 IServiceCollection
删除的注意事项:经过一番探究,我相信这是因为 ServiceDescriptor
doesn't provide an equality or comparison method other than Object.Equals
,这又回到了引用相等性。因此,即使新 ServiceDescriptor
中的所有值都相同,它们也会是不同的对象,因此不会被发现并从服务列表中删除。