.NET Core 在集成测试中停止托管服务



我有.NET Core Web API项目,出于某些原因,我们在此项目中创建了一个后台服务,并在应用程序启动时开始运行后台服务。 因此,我们创建了一个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;
}
}

为了在应用程序启动时运行它,我将后台服务添加到程序中的托管服务中.cs如下所示:

.ConfigureServices(services =>
services.AddHostedService<BackgroundWorkerService>());

现在,我们需要创建一个集成测试,并且我们希望在运行集成测试时停止后台服务。

有谁知道如何在集成测试中停止它?我试图从 ConfigureTestServices 中删除该服务,但没有运气,当集成测试开始时,后台服务仍在运行。

正如您在问题中提到的,您应该能够在ConfigureTestServices中删除该服务。但是,我们指定要删除哪个服务的确切方式是这里的关键。

根据我的经验,您需要找到有问题的ServiceDescriptor,而不是创建一个具有预期值的新(请参阅下面的注释了解原因)。我使用 LINQ 和服务的实现类型(在您的情况下为BackgroundWorkerService)轻松找到现有的ServiceDescriptor。然后我可以将其从列表中删除,这意味着当WebApplicationFactory上调用CreateClient()时它不会启动。

看起来像这样:

builder.ConfigureTestServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundWorkerService));
services.Remove(descriptor);
}

这种方法的一个非常好的好处是,它将测试逻辑排除在生产代码之外,完全保留在测试的设置/夹具中。

关于IServiceCollection删除的说明:稍微戳一下,我相信这是因为ServiceDescriptor除了Object.Equals之外没有提供相等或比较方法,这回退到参考相等。因此,即使新ServiceDescriptor中的所有值都相同,它们也会是不同的对象,因此不会被找到并从服务列表中删除。

其他答案是正确的,但我想添加一点补充,因为我发现了为什么RemoveAll仍然可以使用。

根据 dotnet 源代码,RemoveAll 方法使用ServiceType而不是ImplementationType来查找服务。

/// <summary>
/// Removes all services of type <paramref name="serviceType"/> in <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="collection">The <see cref="IServiceCollection"/>.</param>
/// <param name="serviceType">The service type to remove.</param>
/// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection RemoveAll(this IServiceCollection collection, Type serviceType)
{
ThrowHelper.ThrowIfNull(serviceType);
for (int i = collection.Count - 1; i >= 0; i--)
{
ServiceDescriptor? descriptor = collection[i];
if (descriptor.ServiceType == serviceType)
{
collection.RemoveAt(i);
}
}
return collection;
}

这意味着我们不能使用实际的服务类型BackgroundWorkerService(如问题中所示),而是使用它注册的类型

,即IHostedService

这意味着此代码可用于消除所有后台工作线程的存在。

services.RemoveAll<IHostedService>();

不幸的是,这也删除了一些由 asp.net 框架注册的工人,他们需要重新引入。

我遇到了同样的问题,所以我之前通过覆盖 CreateHost 扩展了 WebApplicationFactory 类并删除了托管服务

public class CustomWebApplicationFactory<T> : WebApplicationFactory<T>
where T : class
{
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.Single(s => s.ImplementationType == typeof(BackgroundService));
services.Remove(descriptor);
});
return base.CreateHost(builder);
}
}

我找到了一个解决方案,可以将后台服务寄存器置于以下条件下。

编辑程序.cs文件,如下所示在注册后台服务部分中:

.ConfigureServices(services =>
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "INTEGRATION")
{
services.AddHostedService<BackgroundWorkerService>();
}
});

然后尝试从您需要的位置将变量更改为集成。

相关内容

  • 没有找到相关文章

最新更新