如何断言任务至少重新启动过一次

本文关键字:重新启动 一次 任务 何断言 断言 | 更新日期: 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
}