在.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
}
}
}
我正在尝试对该类进行单元测试,而我想要做的就是确保在调用DoStuff
方法时调用StartAsync
。这是我的单元测试:
[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);
}
}
为什么会失败?
答案 0 :(得分: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);
}
}
以上示例使用较短的频率来测试预期的行为