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

时间:2013-07-24 20:27:02

标签: c# unit-testing asynchronous synchronization 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)
        });
    }
}

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

[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(...);
}

因此,我需要让单元测试线程等待,直到Bar方法中启动的所有任务在开始我的断言之前完成了它们的工作。

重要:我无法更改SomeClass

我该怎么做?

3 个答案:

答案 0 :(得分:23)

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

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;
    }
}

随后,创建此同步任务调度程序的实例,并使用它来执行根任务,从而生成所有“隐藏”任务。由于嵌套任务从其父级继承当前任务调度程序,因此所有内部任务也将在我们的同步调度程序上运行,这意味着我们最外面的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(...);

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

答案 1 :(得分:5)

Task.WaitAll(the, list, of, task, objects, you, need, to, wait, on);

如果是void async方法,则无法执行此操作。设计破裂了。他们只是为了火而忘记。

答案 2 :(得分:1)

不确定您是否被允许进行此更改,但我已将其付诸实践:

namespace ParallelProgramming.Playground
{
    public class SomeClass
    {
        public Task Foo()
        {
            return Bar();
        }

        private static Task Bar()
        {
            return Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("I fired off. Thread ID: {0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                    return true; //or whatever else you want.
                });
        }
    }

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

            var results = new ConcurrentBag<Task>(); 
            var waitForMe = Task.Factory.StartNew(() =>
                {
                    // Act
                    results.Add(obj.Foo());
                    results.Add(obj.Foo());
                    results.Add(obj.Foo());

                    return true;
                });


            Task.WaitAll(waitForMe);

            // Assert
            /* I need something to wait on all tasks to finish */
            Assert.IsTrue(waitForMe.Result);
            Assert.AreEqual(3, results.Count);
        }
    }
}