如何断言任务至少重新启动过一次
本文关键字:重新启动 一次 任务 何断言 断言 | 更新日期: 2023-09-27 18:21:26
我正在测试驱动一个类,该类被注入了一堆工作任务,异步运行,并在完成后重新启动它们,直到被告知停止所有任务。
由于我首先要做测试,所以我需要写一个测试,迫使我写重启逻辑,我已经成功地完成了这项工作,但我认为我做得不太好。
测试代码:(FakeTask
基本上是一个测试间谍,跟踪它是否被调用以及调用次数)
[Fact]
public async void Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
Thread.Sleep(500);
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1);
}
(相关)生产代码:
public void Start(params IWorkTask[] workTasks)
{
_logWriter.Debug("Starting...");
_tasks = workTasks
.Select(workTask => workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask)))
.ToArray();
}
private void OnTaskComplete(Task completedTask, IWorkTask workTask)
{
if (completedTask.IsFaulted)
{
foreach (var exception in completedTask.Exception.InnerExceptions)
{
_logWriter.Error("Unhandled exception thrown!", exception);
}
}
else workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask));
}
public Task Stop()
{
return Task.WhenAll(_tasks)
.ContinueWith(t => { _logWriter.Debug("Stopped"); });
}
现在的测试实际上取决于比赛条件,感觉根本不像是单元测试。如何摆脱Thread.Sleep(500)
调用?或者这只是我应该在集成测试中测试的东西?
顺便说一下,我建议一般不要编写"任务运行器",也特别建议不要编写ContinueWith
,因为它是一个非常危险的API。
在我看来,"永远重复直到取消"的逻辑使用"重复"的循环和"取消"的取消令牌来更清楚地表达:
static async Task WorkAsync(Func<Task> doWork, CancellationToken token)
{
while (true)
{
await doWork();
token.ThrowIfCancellationRequested();
}
}
然而,也就是说,如果你想按原样对你的"任务运行器"进行单元测试,你需要让你的FakeTask
更加智能。例如,当它达到给定计数时,你可以让它设置一个信号,并让你的单元测试等待:
class FakeTask : IWorkTask
{
private readonly TaskCompletionSource<object> _done = new TaskCompletionSource<object>();
public Task Done { get { return _done.Task; } }
public Task DoWork()
{
++TimesRun;
if (TimesRun > 1)
_done.TrySetResult(null);
return Task.CompletedTask;
}
}
[Fact]
public async Task Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
await fakeTask.Done;
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1); // spurious test at this point
}