77

我有一个 ASP.NET Web API 应用程序,它带有一个具有异步方法、返回Task<>对象并用async关键字标记的 ApiController。

public class MyApiController : ApiController
{
    public async Task<MyData> GetDataById(string id)
    {
        ...
    }
}

如何为 ApiController 的异步方法编写 NUnit 测试?如果我需要使用另一个测试框架,我也愿意。一般来说,我对 .NET 单元测试还很陌生,所以我对学习最佳实践很感兴趣。

4

5 回答 5

77

截至今天(2014 年 7 月 2 日),异步测试受以下支持:

在前两个框架​​中,测试方法必须有这个签名:

[TestMethod]
public async Task MyTestMethod()
{
   ...
   var result = await objectUnderTest.TestedAsyncMethod(...);
   // Make assertions
}

NUnit v2.6.2+(但在 3.0 之前),除了那个签名,还支持这个:

public async void MyTestMethod()

当然,在任何这些测试方法中,您都可以使用await来调用和等待异步方法。

如果您使用的测试框架不支持异步测试方法,那么唯一的方法是调用异步方法并使用任何常用方法等待它完成运行:await,读取Result属性由方法Task<T>返回的async,使用任何常用的等待方法,Task依此类推。在等待之后,您可以像往常一样执行所有断言。例如,使用 MSTest:

[TestMethod]
public void MyTestMethod()
{
    ...
    Task<MyResultClass> task = objectUnderTest.MyAsyncMethod(...);
    // Make anything that waits for the method to end
    MyResultClass result = task.Result;

    // Make the assertions
    Assert.IsNotNull(result);
    ...
}
于 2014-02-07T01:01:34.997 回答
21

在我看来,NUnit 2.6 中不支持测试返回任务的异步方法。我现在能看到的最佳选择是使用 Visual Studio 自己的 UnitTesting 框架或 xUnit.net ,因为它们都支持异步测试

使用 Visual Studio UnitTesting 框架,我可以编写如下异步测试:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestAsyncMethods
{
    [TestMethod]
    public async Task TestGetBinBuildById()
    {
         ...
         var rslt = await obj.GetAsync();
         Assert.AreEqual(rslt, expected);
    }
}
于 2012-09-03T12:47:13.250 回答
6

请记住,NUnit 2.6 系列与之前的系列一样,都是针对 .NET 2.0 框架而构建的。因此,NUnit 只能在面向 .NET 2.0 的程序集中执行您自己编写的代码。

具体来说,您不能将您的测试方法标记为异步并期望 NUnit 对其进行任何特殊处理。

但是,您可以

  • 以 .NET 4.5 为目标进行测试。NUnit 将在单独的进程中运行它们。
  • 在测试中使用await来等待调用 异步方法的结果。

如果这是您所希望的,以上都不会为您提供异步测试执行。在等待异步操作完成时不会执行其他测试。

另一种选择是使用 NUnitLite。NUnitLite 0.8 支持 [Asynchronous] 属性,这将允许其他测试在您的异步测试完成时继续。该属性的优点是它允许异步测试在 .NET 2.0 到 4.0 中工作

我们目前没有 NUnitLite 的 .NET 4.5 版本,但很快就会添加它,我们正在努力进行更改,以在该环境中使用 [Asynchronous] 可选。现在,您可以轻松下载并重新编译 .NET 4.5 的源代码。

对于未来,期待 NUnit 3.0 完全支持异步方法以及在多个线程或多个进程中的通用并行执行测试。

于 2012-09-28T15:48:45.127 回答
4

我正在将我的一些方法转换为async. 让它与 NUnit 一起工作非常简单。

测试方法不能是异步的。但是我们仍然可以访问任务并行库的全部功能,只是不能await在测试方法中直接使用关键字。

在我的示例中,我有一个方法:

public string SendUpdateRequestToPlayer(long playerId)

它在 NUnit 中进行了如下测试:

string result = mgr.SendUpdateRequestToPlayer(player.Id.Value);
Assert.AreEqual("Status update request sent", result);
mocks.VerifyAll();

现在我已经将方法更改SendUpdateRequestToPlayer为异步

public async Task<string> SendUpdateRequestToPlayer(long playerId)

我只需要修改我的测试来Wait完成任务:

Task<string> task = mgr.SendUpdateRequestToPlayer(player.Id.Value);
task.Wait(); // The task runs to completion on a background thread
Assert.AreEqual("Status update request sent", task.Result);
mocks.VerifyAll();
于 2012-09-25T04:31:39.823 回答
2

这真的取决于他们在做什么。通常他们将依赖于提供 a或类似的东西的其他东西。Task<T>在这种情况下,您可能能够提供虚假的依赖项,从而允许您以细粒度的方式控制所有这些。我有一个原型“时间机器”,它允许您对任务进行预编程以在特定的人工时间完成,然后将时间向前移动并随时执行断言。有一篇关于它的博客文章,您可能会发现它很有用。正如我所说,它只是一个原型,并不适合所有场景——但它可能适合你。

Stephen Cleary 也有几篇关于单元测试的博客文章(1、2 ,采用了稍微不同的方法,以及一个您可能会发现有用的NuGet 包。

基本方法与正常方法相同:为您的方法提供不同的输入(和依赖项输出)并测试结果。用异步实现这一点肯定更棘手,但它是可行的。

于 2012-08-30T07:39:25.190 回答