如何编写单元测试用例的异步方法? [英] How to write unit test cases for async methods?
问题描述
我想用嘲讽的依赖性写单元测试案例。整体流程如下。
我们有一个 WorklistLoader
其中有一个异步方法 LoadWorklistItemsAsync()
。为了完成这一任务 WorklistLoader
依赖于底层的API(我想嘲弄) QueryManager.StartQueryTask()
。 StartQueryTask()
也是该查询的文件系统,并提出了一个异步方法的 ProgressChanged()
定期,然后在最后提出了一个 CompletedEvent
。 StartQueryTask()
返回对TPL 工作
。
签署的 StartQueryTask
是
任务StartQueryTask(
SomeId
事件处理程序< ProgressChanged> progressChanged,
事件处理程序< QueryCompleted> queryCompleted);
一旦 WorklistLoader
临危从 QueryManager
ProgressChanged 事件>,它做了一些处理,然后提高它的 ProgressChanged
事件(到了视图模型
已认购)。
我想测试 WorklistLoader
的 LoadWorklistItemsAsync()
法嘲讽 QueryManager .StartQueryTask()
。
下面是我的问题。
- 什么是编写单元测试的最佳实践
异步()
与嘲讽? 方法
- 如何编写单元测试用例的方法,其相关使用TPL?(返回方法
工作
键入)
另一个问题是,
- 如果我使用Rhinomocks嘲笑我QueryManager.StartQueryTask()方法如何会是什么样子? (嘲讽code,具有提高progresschanged,已完成的事件,并返回任务)。
为了嘲笑的东西,你需要能够到无论你使用的是模拟注入。有很多方法可以做到这一点,有控制的容器,环境方面的引导code等最简单的方法就是构造函数注入和引导你的周围环境有你想要的模拟,当你想要测试的反转。例如:
WorklistLoader worklistLoader;
[建立]
公共无效设置()
{
worklistLoader =新WorklistLoader(新MockQueryManager());
}
[测试]
公共异步任务TestWorklistLoader()
{
等待worklistLoader.LoadWorklistItemsAsync();
}
这也意味着,WorklistLoader不依赖于 QueryManager
而是取决于像 IQueryManager
的<$抽象C $ C> MockQueryManager 将实施。
其中, MockQueryManager
可能是这样的:
公共类MockQueryManager:IQueryManager
{
公共任务StartQueryTask(){/ * TODO:* /}
}
当然,你原来的QueryManager将不得不实施IQueryManagear:
公共类QueryManager:IQueryManager
{
公共任务StartQueryTask(){/ * TODO:* /}
}
现在,在测试中TPL-使用类的术语,你会发现,我实现了一个异步的测试方法,该方法返回一个任务。这告诉测试运行前想测试方法执行,以等待结果。如果你只是写的是这样的:
[测试]
公共异步无效TestWorklistLoader()
{
等待worklistLoader.LoadWorklistItemsAsync();
}
亚军将执行 TestWorklistLoader
键,它会在 LoadWorklistItemsAsync
立即返回完成,并有可能绕过任何断言。
更新:
如果你不使用C#5,那么我建议你只是在等待任务的单元测试中完成。例如:
[测试]
公共无效TestWorklistLoader()
{
变种任务= worklistLoader.LoadWorklistItemsAsync();
如果task.Wait()(task.IsComplete()!);
}
I would like to write a unit test case by mocking the dependencies. The overall flow is as follows.
We have a WorklistLoader
which has an async method LoadWorklistItemsAsync()
. To accomplish this task WorklistLoader
is dependent on lower layer API(which i want to mock) QueryManager.StartQueryTask()
. StartQueryTask()
is also a async method which queries the file system and raises the ProgressChanged()
at regular intervals and then at the end raises the CompletedEvent
. StartQueryTask()
returns a reference to TPL Task
.
Signature of the StartQueryTask
is
Task StartQueryTask(
"SomeId",
EventHandler<ProgressChanged> progressChanged,
EventHandler<QueryCompleted> queryCompleted);
Once the WorklistLoader
recieves the ProgressChanged
event from QueryManager
, it does some processing and then raises its ProgressChanged
event(to which the ViewModel
has subscribed).
I would like to test LoadWorklistItemsAsync()
method of the WorklistLoader
with mocking QueryManager.StartQueryTask()
.
Here are my questions.
- What is the best practice to write unit test for the
Async()
methods with mocking? - How to write unit test case for methods whose dependencies uses TPL ?(methods returning
Task
type)
Another question is
- If i mock my QueryManager.StartQueryTask() method using Rhinomocks How would it look like ? (The mocking code. It has to raise progresschanged, completed events and return Task).
In order to mock something, you need to be able to inject the mock into whatever you are using. There are many ways you can do this, with Inversion of Control containers, ambient context bootstrap code, etc. The easiest way is to constructor injection and bootstrap your ambient context to have the mock you want when you want to test. For example:
WorklistLoader worklistLoader;
[SetUp]
public void Setup()
{
worklistLoader = new WorklistLoader(new MockQueryManager());
}
[Test]
public async Task TestWorklistLoader()
{
await worklistLoader.LoadWorklistItemsAsync();
}
This also means that WorklistLoader doesn't depend on QueryManager
but depends on an abstraction like IQueryManager
that MockQueryManager
would implement.
Where MockQueryManager
might be something like:
public class MockQueryManager : IQueryManager
{
public Task StartQueryTask() {/* TODO: */}
}
And of course your original QueryManager would have to implement IQueryManagear:
public class QueryManager : IQueryManager
{
public Task StartQueryTask() {/* TODO: */}
}
Now, in terms of testing TPL-using classes, you'll notice that I've implemented an async test method that returns a Task. This tells test runners to wait for the result before thinking the test method has executed. If you simply wrote something like:
[Test]
public async void TestWorklistLoader()
{
await worklistLoader.LoadWorklistItemsAsync();
}
The runner would execute TestWorklistLoader
and it would return immediately before LoadWorklistItemsAsync
completed and possibly bypass any asserts.
Update:
If you're not using C# 5, then I'd recommend simply waiting for the task to complete within the unit test. For example:
[Test]
public void TestWorklistLoader()
{
var task = worklistLoader.LoadWorklistItemsAsync();
if(!task.IsComplete()) task.Wait();
}
这篇关于如何编写单元测试用例的异步方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!