如何为后台服务编写单元测试?

时间:2019-06-18 08:17:08

标签: c# .net-core asp.net-core-hosted-services

我正在使用.NET Core(不是WebHost!)中的HostBuilder。

我的应用程序中运行了一个托管服务,该托管服务覆盖了后台服务的ExecuteAsync / StopAsync方法,并且我想对其进行单元测试。

这是我的HostedService:

    public class DeviceToCloudMessageHostedService : BackgroundService
    {
        private readonly IDeviceToCloudMessageService _deviceToCloudMessageService;
        private readonly AppConfig _appConfig;

        public DeviceToCloudMessageHostedService(IDeviceToCloudMessageService deviceToCloudMessageService, IOptionsMonitor<AppConfig> appConfig)
        {
            _deviceToCloudMessageService = deviceToCloudMessageService;
            _appConfig = appConfig.CurrentValue;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await _deviceToCloudMessageService.DoStuff(stoppingToken);
                await Task.Delay(_appConfig.Parameter1, stoppingToken);
            }
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            Log.Information("Task Cancelled");
            _deviceToCloudMessageService.EndStuff();
            return base.StopAsync(cancellationToken);
        }

我已经找到了该帖子Integration Test For Hosted Service .Net-Core` 但这是针对QueuedBackgroundService进行的解释,我真的不知道是否可以用相同的方式测试我的。

我只想知道我的代码是否已执行。我不想要任何特定的结果。 您是否知道如何测试? 非常感谢。

1 个答案:

答案 0 :(得分:0)

您仍然应该能够采用与链接答案相似的格式。

模拟依赖项并将其注入,调用测试中的方法并声明预期的行为。

以下内容使用Moq来模拟依赖项,并使用ServiceCollection来进行注入依赖项的繁重工作。

[TestMethod]
public async Task DeviceToCloudMessageHostedService_Should_DoStuff() {
    //Arrange
    IServiceCollection services = new ServiceCollection();
    services.AddSingleton<IHostedService, DeviceToCloudMessageHostedService>();
    //mock the dependencies for injection
    services.AddSingleton(Mock.Of<IDeviceToCloudMessageService>(_ =>
        _.DoStuff(It.IsAny<CancellationToken>()) == Task.CompletedTask
    ));
    services.AddSingleton(Mock.Of<IOptionsMonitor<AppConfig>>(_ =>
        _.CurrentValue == Mock.Of<AppConfig>(c => 
            c.Parameter1 == TimeSpan.FromMilliseconds(1000)
        )
    ));
    var serviceProvider = services.BuildServiceProvider();
    var hostedService = serviceProvider.GetService<IHostedService>();

    //Act
    await hostedService.StartAsync(CancellationToken.None);
    await Task.Delay(1000);//Give some time to invoke the methods under test
    await hostedService.StopAsync(CancellationToken.None);

    //Assert
    var deviceToCloudMessageService = serviceProvider
        .GetRequiredService<IDeviceToCloudMessageService>();
    //extracting mock to do verifications
    var mock = Mock.Get(deviceToCloudMessageService);
    //assert expected behavior
    mock.Verify(_ => _.DoStuff(It.IsAny<CancellationToken>()), Times.AtLeastOnce);
    mock.Verify(_ => _.EndStuff(), Times.AtLeastOnce());
}

现在,理想情况下,这将被视为测试框架代码,因为您基本上是在测试BackgroundService在运行时的行为是否符合预期,但是它应该充分说明如何单独测试这样的服务

相关问题