[英]async task testing
I just started using Rhino Mocks that test my async methods within the code. 我刚刚开始使用Rhino Mocks在代码中测试我的异步方法。 But while normally the code works just perfect but while mocking tests with Rhino Mocks there are some weird
async
problems. 虽然通常代码工作得很完美但是在使用Rhino Mocks进行模拟测试时会出现一些奇怪的
async
问题。
Let's say I want to test the following code which calls multiple numberGenerators
that all will run at the same time and wait till they are all done: 假设我想测试以下代码,该代码调用多个
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
}
Is tested with: 测试用:
numberGenerator.Expect(x => x.GetNumbers())
.WhenCalled(x => Thread.Sleep(10000))
.Return(Task.FromResult(numbers));
I use two of the generators on 'mocked' in the code above. 我在上面的代码中使用了'mocked'中的两个生成器。 Now if I run the code with 'non-mocked' objects
numberTasks.Add
just adds the Task and moves on. 现在,如果我使用'非模拟'对象运行代码
numberTasks.Add
只需添加任务并继续。 But when I mock it with the WhenCalled sleep
it waites for 10 seconds before going to the next one in the foreach loop. 但是,当我用
WhenCalled sleep
模拟它时,它等待10秒钟,然后转到foreach循环中的下一个。
How can I change my mock that it acts like a normal async Task
that takes 10 seconds to complete? 如何更改我的模拟,它像一个需要10秒才能完成的正常
async Task
?
Bonus question: Can want to be able to do the same with .Throw()
so I can wait 10 seconds before throwing an Exception. 奖金问题:可以希望能够使用
.Throw()
做同样的事情,这样我可以在抛出异常之前等待10秒。
Update: Although I thought the following would fix the problem: 更新:虽然我认为以下将解决问题:
numberGenerator.Expect(x => x.GetNumbers())
.Return(Task.Delay(10000).ContinueWith(x => numbers));
This doesn't seem to be the case. 似乎并非如此。 The Task.Delay starts counting down at the moment this
mock
is created. 在创建此
mock
时,Task.Delay开始倒计时。 So if I create this mock, wait 10 seconds and then run the test it will be done in 0 seconds. 所以如果我创建这个模拟,等待10秒然后运行测试它将在0秒内完成。
So my question remains, how can I test an async method with a delay
before returning the result. 所以我的问题仍然存在,如何在返回结果之前测试一个
delay
的异步方法。
An async method runs synchronously until an await
is reached. 异步方法同步运行,直到达到
await
。 That means if you wait before returning a task in the mocked operation this wait will be synchronous. 这意味着如果您在模拟操作中返回任务之前等待,则此等待将是同步的。 What you want to do is return a task immediately that completes after some time.
你想要做的是立即返回一个任务,一段时间后完成。
You can do that with Task.Delay
: 你可以使用
Task.Delay
做到这Task.Delay
:
numberGenerator.Expect(x => x.GetNumbers()).
Return(Task.Delay(10000).ContinueWith(t => numbers));
Task.ContinueWith
is used to add the actual return value after the delay is completed. Task.ContinueWith
用于在延迟完成后添加实际返回值。
If you would like to throw an exception instead of returning a value, that too can be done inside the continuation. 如果您想抛出异常而不是返回值,那么也可以在continuation中完成。
Since Task.Delay(10000).ContinueWith(t => numbers)
is evaluated when you create the mock, each call would return the same task (which will be completed 10 seconds after the mock was created). 由于在创建模拟时会评估
Task.Delay(10000).ContinueWith(t => numbers)
,因此每次调用都将返回相同的任务(将在创建模拟后10秒完成)。
If you need to create a new task each time you need to pass a delegate. 如果每次需要传递委托时需要创建新任务。
Return
doesn't accept a delegate so you need to use WhenCalled
: Return
不接受委托,因此您需要使用WhenCalled
:
numberGenerator.Expect(x => x.GetNumbers()).
WhenCalled(mi => mi.ReturnValue = Task.Delay(10000).ContinueWith(x => numbers));
Now, while WhenCalled
is used to set the return value using Return
is still required in order to determine the type of the return result . 现在,当使用
WhenCalled
来设置返回值时,仍然需要使用Return
来确定返回结果的类型 。 It doesn't matter what value it gets as the return value will still be set by WhenCalled
: 它得到的值无关紧要,因为返回值仍将由
WhenCalled
设置:
numberGenerator.Expect(x => x.GetNumbers()).
WhenCalled(mi => mi.ReturnValue = Task.Delay(10000).ContinueWith(x => numbers)).
Return(Task.FromResult(new List<int>()));
Personally I haven't worked with rhino mock, so I don't know if it supports async Method calls. 我个人没有使用过rhino mock,所以我不知道它是否支持异步Method调用。
But Thread.Sleep(10000)
is a synchronous code. 但是
Thread.Sleep(10000)
是一个同步代码。 If you want await in asynchronously you have to use Task.Delay(10000)
. 如果要异步等待,则必须使用
Task.Delay(10000)
。
Or alternatively use Task.Run( () => Thread.Sleep(10000) )
though, Task.Delay(10000)
is preferred way and best practice. 或者使用
Task.Run( () => Thread.Sleep(10000) )
但Task.Delay(10000)
是首选方式和最佳实践。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.