如何编写单元测试用例的异步方法? [英] How to write unit test cases for async methods?

查看:323
本文介绍了如何编写单元测试用例的异步方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用嘲讽的依赖性写单元测试案例。整体流程如下。

我们有一个 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()

下面是我的问题。

  1. 什么是编写单元测试的最佳实践异步()与嘲讽?
  2. 方法
  3. 如何编写单元测试用例的方法,其相关使用TPL?(返回方法工作键入)

另一个问题是,

  1. 如果我使用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.

  1. What is the best practice to write unit test for the Async() methods with mocking?
  2. How to write unit test case for methods whose dependencies uses TPL ?(methods returning Task type)

Another question is

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆