英文:
Unit test .net 6 console app using IHostApplicationLifeTime
问题
我有一个类(`CronClass`)继承自`IHostedService`,其中包括两个方法:`StartAsync(CancellationToken)`和`StopAsync(CancellationToken)`。
你如何使用Moq库来进行单元测试以验证`StartAsync`方法是否执行了代码?
例如:
```csharp
public class CronClass: IHostedService
{
private readonly IHostedApplicationLifetime applicationLifetime;
private readonly IService service;
// IHostedApplicationLifetime/IService 通过构造函数进行依赖注入
public Task StartAsync(CancellationToken cancellationToken)
{
applicationLifeTime.ApplicationStarted.Register(() =>
{
Task.Run(async () =>
{
log.LogInformation("Cron Started");
await service.Process();
});
});
}
//...
}
英文:
I have a class (CronClass
) that inherits from IHostedService
with 2 methods which are StartAsync(CancellationToken)
and StopAsync(CancellationToken)
.
How do you go about unit testing the StartAsync
method to verify that the code was executed, using Moq Library?
For example:
public class CronClass: IHostedService
{
private readonly IHostedApplicationLifetime applicationLifetime;
private readonly IService service;
// IHostedApplicationLifetime/IService are injected DI to via the constructor
public Task StartAsync(CancellationToken cancellationToken)
{
applicationLifeTime.ApplicationStarted.Register(() =>
{
Task.Run(async () =>
{
log.LogInformation("Cron Started");
await service.Process();
});
});
}
//...
}
答案1
得分: 2
以下是代码部分的中文翻译:
我会从创建 `IHostApplicationLifetime` 的模拟开始
public class MockHostApplicationLifetime : IHostApplicationLifetime, IDisposable
{
internal readonly CancellationTokenSource _ctsStart = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopped = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopping = new CancellationTokenSource();
public MockHostApplicationLifetime()
{
}
public void Started()
{
_ctsStart.Cancel();
}
CancellationToken IHostApplicationLifetime.ApplicationStarted => _ctsStart.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopping => _ctsStopping.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopped => _ctsStopped.Token;
public void Dispose()
{
_ctsStopped.Cancel();
_ctsStart.Dispose();
_ctsStopped.Dispose();
_ctsStopping.Dispose();
}
public void StopApplication()
{
_ctsStopping.Cancel();
}
}
在你的单元测试中创建 `IService` 的模拟。创建 `CronClass` 的实例并调用 `cronClass.StartAsync`。然后启动 `MockHostApplicationLifetime`。这将触发注册的回调函数 `ApplicationStarted.Register`。然后验证是否调用了 `Process()`。
你在 `Register` 方法中启动了任务,所以可能会发生在单元测试完成之前任务被创建并调用了 `service.Process` 的情况。在这种情况下,我建议在验证之前等待一段时间。
[Test]
public async Task Test1()
{
var hal = new MockHostApplicationLifetime();
var mockService = new Mock<IService>();
var cronClass = new CronClass(hal, mockService.Object);
await cronClass.StartAsync(CancellationToken.None);
hal.Started();
// 可能不需要,但即使没有延迟,测试也通过了
// await Task.Delay(500);
mockService.Verify(mock => mock.Process());
}
英文:
I would start with creating a mock of IHostApplicationLifetime
public class MockHostApplicationLifetime : IHostApplicationLifetime, IDisposable
{
internal readonly CancellationTokenSource _ctsStart = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopped = new CancellationTokenSource();
internal readonly CancellationTokenSource _ctsStopping = new CancellationTokenSource();
public MockHostApplicationLifetime()
{
}
public void Started()
{
_ctsStart.Cancel();
}
CancellationToken IHostApplicationLifetime.ApplicationStarted => _ctsStart.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopping => _ctsStopping.Token;
CancellationToken IHostApplicationLifetime.ApplicationStopped => _ctsStopped.Token;
public void Dispose()
{
_ctsStopped.Cancel();
_ctsStart.Dispose();
_ctsStopped.Dispose();
_ctsStopping.Dispose();
}
public void StopApplication()
{
_ctsStopping.Cancel();
}
}
In your unit test create a mock of IService
. Create instance of CronClass
and call cronClass.StartAsync
. Then start MockHostApplicationLifetime
. It will trigger registered callback ApplicationStarted.Register
. Then verify that Process()
was called.
You are starting the task in Register
method, so it can happen that the unit test can finish before the task is created and service.Process
is called. In that case I would wait some time before verification.
[Test]
public async Task Test1()
{
var hal = new MockHostApplicationLifetime();
var mockService = new Mock<IService>();
var cronClass = new CronClass(hal, mockService.Object);
await cronClass.StartAsync(CancellationToken.None);
hal.Started();
// maybe not needed, test passed without the delay
//await Task.Delay(500);
mockService.Verify(mock => mock.Process());
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论