等到所有任务完成单元测试

mmu*_*lva 13 c# synchronization unit-testing asynchronous task

我有这个课我要单元测试:

public class SomeClass
{
    public void Foo()
    {
        Bar();
    }

    private void Bar()
    {
        Task.Factory.StartNew(() =>
        {
            // Do something that takes some time (e.g. an HTTP request)
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我的单元测试的样子:

[TestMethod]
public void TestFoo()
{
    // Arrange
    var obj = new SomeClass();

    // Act
    obj.Foo();
    obj.Foo();
    obj.Foo();

    // Assert
    /* I need something to wait on all tasks to finish */
    Assert.IsTrue(...);
}
Run Code Online (Sandbox Code Playgroud)

所以,我需要让单元测试线程等到Bar方法中启动的所有任务都完成了他们的工作,然后再开始我的断言.

重要提示:我无法改变SomeClass

我怎样才能做到这一点?

Dou*_*las 25

解决此问题的一种方法是以这样一种方式定义您自己的任务调度程序,以便您可以跟踪嵌套任务的完成情况.例如,您可以定义一个同步执行任务的调度程序,如下所示:

class SynchronousTaskScheduler : TaskScheduler
{
    protected override void QueueTask(Task task)
    {
        this.TryExecuteTask(task);
    }

    protected override bool TryExecuteTaskInline(Task task, bool wasPreviouslyQueued)
    {
        return this.TryExecuteTask(task);
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        yield break;
    }
}
Run Code Online (Sandbox Code Playgroud)

随后,创建此同步任务调度程序的实例,并使用它来执行根任务,从而生成所有"隐藏"任务.由于嵌套任务从其父级继承当前任务调度程序,因此所有内部任务也将在我们的同步调度程序上运行,这意味着我们的最外层StartNew调用只会在所有任务完成时返回.

TaskScheduler scheduler = new SynchronousTaskScheduler();

Task.Factory.StartNew(() =>
{
    // Arrange
    var obj = new SomeClass();

    // Act
    obj.Foo();
    obj.Foo();
    obj.Foo();
}, 
    CancellationToken.None,
    TaskCreationOptions.None,
    scheduler);

// Assert
/* I need something to wait on all tasks to finish */
Assert.IsTrue(...);
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点是你将失去你的任务的所有并发性; 但是,您可以通过将自定义调度程序增强为并发调度程序但仍允许您跟踪执行任务来解决此问题.

  • @JustAMartin:正确。`Task.Run` 相当于 `Task.Factory.StartNew(..., TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)`,因此嵌套任务不会从其父级继承当前任务计划程序。 (3认同)

It'*_*ie. 5

Task.WaitAll(the, list, of, task, objects, you, need, to, wait, on);
Run Code Online (Sandbox Code Playgroud)

如果这是一种void async方法,那么您将无法做到。设计已损坏。他们只是为了生火而忘却。

  • 不幸的是,对于@Toto,任务对象在他的单元测试中不可用 (4认同)