在我的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);
}
VerifyCommand
和GenerateCommand
实际上是实现AsyncRelayCommand
的{{1}}类型,因此它们是ICommand
,我必须在一个单独的线程中运行它们使用async void
进行测试。 Task.Run()
和Method1
实际上分别代表按钮使用的Method2
和ExecuteMethod1CommandAsync
命令。
问题是,如果我分别运行这些测试,它们都会通过。 但是,一旦我尝试同时测试它们,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。解决此问题的最佳方法是什么?
答案 0 :(得分:1)
async void
意味着呼叫者无法确定该活动何时完成。将其包装在Task.Run()
中不会改变它。现在,您有了一个Task
,它可以开始运行某些东西,然后完成工作,因此尽管它调用的async void
方法可能尚未完成,但仍将其标记为完成。
如果不对代码进行某种重新设计,就不会解决此问题。如果您受外部因素(接口/委托签名)的约束而无法使用这些方法void
,请考虑另一端是否期望这些方法在未完成时返回。如果是这样,那么不幸的是,最好的解决方法是撤消您在此处所做的async
工作。
如果没有外部因素,请改用方法async Task
,以便您可以直接await
使用它们(不需要Task.Run
)。
如果有外部因素,但您认为合同允许您在工作未完成的情况下返回,那么我建议将您拥有的方法数量加倍。用async Task
设置一组,然后对它们进行单元测试。然后使async void
方法成为可能仅调用async Task
版本的 thinnest 包装器。 (如果您可以接受将不对这些包装器进行单元测试)。