我刚刚开始使用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
测试异步方法。
答案 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)
是首选方式和最佳做法。