运行多个异步单元测试方法会产生错误,但单独运行它们不会

时间:2018-08-01 11:18:13

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

在我的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实际上是实现AsyncRelayCommand的{​​{1}}类型,因此它们是ICommand,我必须在一个单独的线程中运行它们使用async void进行测试。 Task.Run()Method1实际上分别代表按钮使用的Method2ExecuteMethod1CommandAsync命令。

问题是,如果我分别运行这些测试,它们都会通过。 但是,一旦我尝试同时测试它们ExecuteMethod2CommandAsync就会失败:

  

预期:<“消息”>,实际<>

这显然是因为其中一个测试不等待线程完成,并且在断言步骤中尚未返回该值。我在两个测试方法中都添加了Assert.AreEqual(),然后当我再次将它们同时运行时两个测试都通过了(这进一步证明了这一点)。

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

编辑:当我分别运行它们时,需要花费时间:两者均〜140 ms。当我一起运行它们时:一次通过的时间是〜140毫秒,另一次不是90毫秒(证明了我的发现)

编辑2:执行方法:

System.Threading.Thread.Sleep(5000);

// Executes the action public async void Execute(object parameter) { // Do something try { await execute(parameter); } finally { // Do something } } execute。显然这是发生问题的地方-启动execute(parameter)时,它立即返回到Func<object, Task>中的Task。解决此问题的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

async void意味着呼叫者无法确定该活动何时完成。将其包装在Task.Run()中不会改变它。现在,您有了一个Task,它可以开始运行某些东西,然后完成工作,因此尽管它调用的async void方法可能尚未完成,但仍将其标记为完成。

如果不对代码进行某种重新设计,就不会解决此问题。如果您受外部因素(接口/委托签名)的约束而无法使用这些方法void,请考虑另一端是否期望这些方法在未完成时返回。如果是这样,那么不幸的是,最好的解决方法是撤消您在此处所做的async工作。

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

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