【问题标题】:Unit testing an async method that uses System.Threading.Timer对使用 System.Threading.Timer 的异步方法进行单元测试
【发布时间】:2019-09-12 14:04:37
【问题描述】:

在 .NET Core 中,后台任务实现为 IHostedService。这是我的托管服务:

public interface IMyService {
    void DoStuff();
}

public class MyHostedService : IHostedService, IDisposable
{
    private const int frequency;

    private readonly IMyService myService;

    private Timer timer;

    public MyHostedService(IMyService myService, Setting s)
    {
        this.myService = myService;
        frequency = s.Frequency;
    }

    public void Dispose()
    {
        this.timer?.Dispose();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        this.timer = new Timer(this.DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(this.frequency));
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        this.timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        try
        {
            this.myService.DoStuff();
        }
        catch (Exception e)
        {
            // log
        }
    }
}

我正在尝试对这个类进行单元测试,我只想确保在调用StartAsync 方法时调用DoStuff。这是我的单元测试:

[TestFixture]
public class MyHostedServiceTests
{
    [SetUp]
    public void SetUp()
    {
        this.myService = new Mock<IMyService>();

        this.hostedService = new MyHostedService(this.myService.Object, new Setting { Frequency = 60 });
    }

    private Mock<ImyService> myService;
    private MyHostedService hostedService;

    [Test]
    public void StartAsync_Success()
    {
        this.hostedService.StartAsync(CancellationToken.None);

        this.myService.Verify(x => x.DoStuff(), Times.Once);
    }
}

为什么会失败?

【问题讨论】:

    标签: c# unit-testing nunit moq


    【解决方案1】:

    它失败了,因为异步代码在与验证预期行为的代码不同的线程上执行。这和在计时器有时间被调用之前调用的验证代码的事实。

    在测试异步方法时,大多数情况下的测试也应该是异步的。

    在这种情况下,您还需要等待一段时间以允许计时器调用。

    使用Task.Delay 给计时器足够的时间来执行其功能。

    例如

    [TestFixture]
    public class MyHostedServiceTests {
        [SetUp]
        public void SetUp() {
            this.myService = new Mock<IMyService>();
            this.setting = new Setting { Frequency = 2 };
            this.hostedService = new MyHostedService(this.myService.Object, setting);
        }
    
        private Mock<ImyService> myService;
        private MyHostedService hostedService;
        private Setting setting;
    
        [Test]
        public async Task StartAsync_Success() {
    
            //Act
            await this.hostedService.StartAsync(CancellationToken.None);
            await Task.Delay(TimeSpan.FromSeconds(1));    
            await this.hostedService.StopAsync(CancellationToken.None);
    
            //Assert
            this.myService.Verify(x => x.DoStuff(), Times.Once);
        }
    }
    

    以上示例使用较短的频率来测试预期行为

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-15
      • 1970-01-01
      • 2010-11-13
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 2016-02-21
      • 2020-07-14
      相关资源
      最近更新 更多