异步任务测试

时间:2015-03-06 14:34:55

标签: c# .net async-await rhino-mocks

我刚刚开始使用Rhino Mocks在代码中测试我的异步方法。虽然通常代码工作得很完美但是在使用Rhino Mocks进行模拟测试时会出现一些奇怪的async问题。

假设我想测试以下代码,该代码调用多个numberGenerators,这些代码将同时运行并等待它们全部完成:

public async Task MyTask()
{
    List<Task<List<int>>> numberTasks = new List<Task<List<int>>>();

    foreach (var numberGenerator in numberGenerators)
    {
        // GetNumbers is an 'async Task<List<int>>'
        // As you can see I don't use 'await' so it should just add it and go on.
        numberTasks.Add(numberGenerator.GetNumbers()); 
    }

    try
    {
        await Task.WhenAll(numberTasks);
    }
    catch (Exception e)
    {
        // Log exception if something happens at 'GetNumbers' (like time-out or whatever)
    }

    var numbers = new List<int>();
    numbers.AddRange(numberTasks.Where(task => task.Status == TaskStatus.RanToCompletion).SelectMany(task => task.Result));

    // Do something with the numbers
}

经过测试:

numberGenerator.Expect(x => x.GetNumbers())
    .WhenCalled(x => Thread.Sleep(10000))
    .Return(Task.FromResult(numbers));

我在上面的代码中使用了'mocked'中的两个生成器。现在,如果我使用'非模拟'对象numberTasks.Add运行代码,只需添加任务并继续。但是当我用WhenCalled sleep嘲笑它时它会等待10秒钟,然后再转到foreach循环中的下一个。

如何更改我的模拟,它的行为像普通async Task,需要10秒才能完成?

奖金问题:可以希望能够对.Throw()做同样的事情,这样我就可以在抛出异常之前等待10秒。

更新 虽然我认为以下内容可以解决问题:

numberGenerator.Expect(x => x.GetNumbers())
    .Return(Task.Delay(10000).ContinueWith(x => numbers));

似乎并非如此。 Task.Delay在创建mock时开始倒计时。因此,如果我创建此模拟,请等待10秒然后运行测试,它将在0秒内完成。

所以我的问题仍然存在,如何在返回结果之前使用delay测试异步方法。

2 个答案:

答案 0 :(得分:2)

异步方法同步运行,直到达到await。这意味着如果您在模拟操作中返回任务之前等待,则此等待将是同步的。你想要做的是立即返回一个任务,一段时间后完成。

您可以使用Task.Delay

执行此操作
numberGenerator.Expect(x => x.GetNumbers()).
        Return(Task.Delay(10000).ContinueWith(t => numbers));

Task.ContinueWith用于在延迟完成后添加实际返回值。

如果您想抛出异常而不是返回值,那么也可以在延续中完成。

由于在创建模拟时评估Task.Delay(10000).ContinueWith(t => numbers),因此每次调用都将返回相同的任务(将在创建模拟后10秒完成)。

如果您需要在每次需要传递委托时创建新任务。 Return不接受委托,因此您需要使用WhenCalled

numberGenerator.Expect(x => x.GetNumbers()).
        WhenCalled(mi => mi.ReturnValue = Task.Delay(10000).ContinueWith(x => numbers));

现在,虽然使用WhenCalled来设置使用Return的返回值仍然需要in order to determine the type of the return result。它得到的值无关紧要,因为返回值仍由WhenCalled设置:

numberGenerator.Expect(x => x.GetNumbers()).
    WhenCalled(mi => mi.ReturnValue = Task.Delay(10000).ContinueWith(x => numbers)).
    Return(Task.FromResult(new List<int>()));

答案 1 :(得分:1)

就我个人而言,我还没有使用过rhino mock,所以我不知道它是否支持异步方法调用。

Thread.Sleep(10000)是同步代码。如果要异步等待,则必须使用Task.Delay(10000)

或者使用Task.Run( () => Thread.Sleep(10000) )Task.Delay(10000)是首选方式和最佳做法。

相关问题