【问题标题】:Running multiple async unit test methods produces an error, but running them individually does not运行多个异步单元测试方法会产生错误,但单独运行它们不会
【发布时间】:2018-08-01 11:18:13
【问题描述】:

在我的 WPF MVVM 中,我有两个 单独运行时工作 单元测试,它们测试两个按钮命令方法:

[TestMethod]
public async Task TestMethod1()
{
    // Arrange
    var interfaceStub = new StubInterface();
    interfaceStub.Method = () => "Message";
    var viewModel = new ViewModel(interfaceStub);

    // Act
    await Task.Run(() => viewModel.GenerateCommand.Execute());

    // Assert
    Assert.AreEqual("Message", viewModel.Response);
}

[TestMethod]
public async Task TestMethod2()
{
    // Arrange
    var interfaceStub = new StubInterface();
    interfaceStub.Method = () => "Message";
    var viewModel = new ViewModel(interfaceStub);

    // Act
    await Task.Run(() => viewModel.VerifyCommand.Execute());

    // Assert
    Assert.AreEqual("Message", viewModel.Response);
}

VerifyCommandGenerateCommand 实际上是实现ICommandAsyncRelayCommand 类型,因此它们是async void,我必须在测试中使用Task.Run() 在单独的线程上运行它们。 Method1Method2 实际上分别代表按钮使用的ExecuteMethod1CommandAsyncExecuteMethod2CommandAsync 命令。

问题是,如果我分别运行这些测试,它们都会通过。 但是,一旦我尝试同时测试它们两个Assert.AreEqual() 失败:

预期: ,实际

这显然是因为其中一个测试没有等待线程完成,并且在断言步骤还没有返回值。我将System.Threading.Thread.Sleep(5000); 添加到两个测试方法中,然后当我再次一起运行它们时,两个测试都通过了(这进一步证明了这一点)。

为什么单独运行它们会使测试方法等待,但单独运行它们会使其中一个不等待?

编辑:当我单独运行它们时,所花费的时间:~140 毫秒。当我一起运行它们时:通过的又是〜140 ms,另一个不通过的只有90 ms(证明我的发现)

编辑2:执行方法:

// Executes the action
public async void Execute(object parameter)
{
    // Do something

     try
     {
         await execute(parameter);
     }
     finally
     {
         // Do something
     }
}

executeFunc<object, Task>。这显然是问题发生的地方——当执行(参数)启动时,它会立即返回到TestMethod 中的任务。解决这个问题的最佳方法是什么?

【问题讨论】:

  • 在测试资源管理器中的 Visual Studio 上,您可以在搜索旁边输入一个带有“并行运行测试”的按钮。删除async修饰符,试试这个方法并行运行单元测试。
  • @Sidron 我无法删除异步...我必须await 才能完成任务。并行运行没有帮助。
  • @Damien_The_Unbeliever 是的,这正是发生的事情,几分钟前我才意识到这一点,因为我在 async void 中的 await 前后运行了一个无限循环..
  • 几乎在所有情况下,await Task.Run() 都是毫无意义的。您要求 TPL 找到 其他线程 来运行一些代码,然后宣布(使用await)-“好吧,对于 this 线程没有任何用处做,直到其他工作完成”。去掉中间人 - 使用 your 线程来运行该代码。
  • 是的,我通常会做的例外是 - 如果您有一个 CPU 绑定操作,您需要在不需要访问 UI 对象的地方执行,并且您当前在 UI 上下文中执行,that 是适合 await Task.Run() 的时间。 (或其他单线程上下文)。但根据我的大部分经验,人们在使用它时没有考虑它是否合适。

标签: c# .net unit-testing microsoft-fakes


【解决方案1】:

async void 表示调用者无法确定该活动何时完成。将其包装在 Task.Run() 中不会改变这一点。您现在有一个 Task 开始运行,然后它的工作已完成,因此它被标记为完成,尽管它调用的 async void 方法可能尚未完成。

如果不对代码进行某种重新设计,您将无法解决此问题。如果您受外部因素(接口/委托签名)的限制而无法将这些方法设置为void,请考虑另一边的任何东西是否会期望这些方法在它们尚未完成时返回。如果是这种情况,那么不幸的是,最好的方法是撤消您在那里所做的async 操作。

如果没有外部因素,请改用async Task方法,以便您可以直接await它们(不需要Task.Run)。

如果存在外部因素,但您认为合同允许您在未完成工作的情况下返回,那么我建议您将拥有的方法数量增加一倍。通过async Task 制作一组并进行单元测试。然后让async void 方法成为最薄 可能的包装器,它只调用async Task 版本。 (如果您可以接受这些包装器不会进行单元测试)。

【讨论】:

  • 包装的东西基本上就是我要找的东西
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
  • 2019-02-07
相关资源
最近更新 更多