【问题标题】:Async CTP, unit testing a ViewModel's async Method异步 CTP,单元测试 ViewModel 的异步方法
【发布时间】:2011-11-07 19:27:34
【问题描述】:

我有一个像这样的单元测试(使用 MSTest):

[TestMethod]
public void MyTest()
{
    var viewModel = new MyViewModel();
    viewModel.Run();
    //Assert something here
}

Run 是一个返回 void 的异步方法。

假设Run 是这样实现的:

public async void Run()
{
    //Show a busy indicator here

    try
    {
        var result = await myAsyncModelClass.LongRunningOperation();

        //Use the results here
    }
    finally
    {
        //Hide the busy indicator here
    }
}

myAsyncModelClass.LongRunningOperation(),它本身就是一个异步方法,它返回一些 Task<T>,其中 T 是我的 ViewModel 感兴趣的结果。

我的问题是,我的测试正在异步运行 Run 方法,因此在 Run 方法完成之前调用了我的断言。奇怪的是,当我放置断点时,b/c 永远不会到达 finally 块,因为断言失败。如何保持Run 方法同步以便能够对其进行单元测试?

我也有一个myAsyncModelClass.LongRunningOperation() 的单元测试,但我只调用Task<T>.Wait(),因为它返回一个任务。这使得它在单元测试时同步。

另外,我想提一下,Run() 是由一个 ICommand 由一个 MVVM 框架神奇地调用的。 void 可能是也可能不是需要返回类型,我将不得不尝试一下。

【问题讨论】:

  • 嗨 - 我对 C#-async 没有经验(杀死了我的 VS 安装),但如果它是异步函数,你可以调用 Run 吗? F# 有一些类似的结构,但如果您尝试调用 asycn 对象,则会出现编译错误。 - 无论如何,你为什么不把它包装成一个任务并等待它(或者你在测试中的等待)?
  • 我会返回一个任务,但是 MVVM 框架也通过绑定到 ICommand 来调用此方法。如果我进行更改,我不相信我的代码会在我的应用程序中执行。

标签: c# .net wpf mvvm async-ctp


【解决方案1】:

异步方法需要一个上下文来“返回”。由于 MSTest 在线程池上运行,默认情况下,异步方法也会在线程池线程上继续运行(并且不会阻塞 MSTest 方法)。

(C# Testing) Unit Testing 示例下(在您的Async CTP 安装目录中),有一个名为GeneralThreadAffineContext 的类型,可以这样使用:

[TestMethod]
public void MyTest()
{
  MyViewModel viewModel = null;
  GeneralThreadAffineContext.Run(() =>
  {
    viewModel = new MyViewModel();
    viewModel.Run();
  });
  //Assert something here
}

还有特定的 WPF 和 WinForms 上下文,但线程仿射上下文应该适用于一般 ViewModel(不明确使用 Dispatcher)。

2012-02-05 更新:如果您可以更改 ViewModel 方法以返回 Task,那么您还有另一个选择:新的 AsyncUnitTests library。安装那个 NuGet 包,将你的 TestClass 更改为 AsyncTestClass,你的异步单元测试可以更自然地编写:

[TestMethod]
public async void MyTest()
{
  MyViewModel viewModel = new MyViewModel();
  await viewModel.Run();
  //Assert something here
}

2012-09-04 更新:Visual Studio 2012 包含 async 单元测试,因此您不再需要 AsyncUnitTests 库:

[TestMethod]
public async Task MyTest()
{
  MyViewModel viewModel = new MyViewModel();
  await viewModel.Run();
  //Assert something here
}

【讨论】:

  • 效果很好。不过,我想我将构建自己的 GeneralThreadAffineContext 实现。
  • 如果你有兴趣,我已经发布了我的 part of Nito.AsyncEx 库,名为 AsyncContext
  • 如果有办法为每个单元测试全局连接它会更好。就像您可以将 async 关键字放在您的单元测试中,MSTest 会处理它。也许在完整的 C# 5 中我们会得到类似的东西。
【解决方案2】:

由于 Visual Studio 2012 MSTest 支持异步测试方法。请记住,他们应该返回 Task 而不是 void:

[TestMethod]
public async Task MyTest()
{
  MyViewModel viewModel = new MyViewModel();
  await viewModel.Run();
  //Assert something here
}

【讨论】:

    猜你喜欢
    • 2020-07-14
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    相关资源
    最近更新 更多