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

时间:2015-11-11 11:36:16

标签: c# unit-testing task

我正在测试驱动一个注入了大量工作任务的类,异步运行并在完成后重新启动它们,直到被告知停止所有任务。

因为我首先要做测试,所以我需要编写一个强制我编写重启逻辑的测试,并且我已经成功完成了这项工作,但我认为我做得不是很好。

测试代码:( 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)电话?或者这只是我应该在集成测试中测试的东西?

1 个答案:

答案 0 :(得分:2)

另一方面,我建议不要一般性地编写“任务运行者”,并建议特别针对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
}