简体   繁体   English

等待在单元测试用例中完成void异步方法

[英]Wait for void async method to complete in unit test case

These are my business engine's methods. 这些是我的业务引擎的方法。 Upload is calling internally async method UploadAsync() 上传是在内部调用异步方法UploadAsync()

public void Upload(Stream data)
{  
   //Some logic
   //Call private async method
   UploadAsync(data);
}

private async void UploadAsync(Object data)
{
    await Task.Run(() =>
    {
               using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
               {
                   factory.Repository.UploadData(data);
               }
           }
    );
}

This is unit test case for Upload() method 这是Upload()方法的单元测试用例

[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
    //other logic to setup mock repository
    //call public method which calls async method
    engine.Upload(data);

   _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}

This test case is getting failed occasioally at verify method with following exception: 此测试用例在验证方法偶尔失败,但有以下异常:

016-10-06T19:25:20.4982657Z ##[error]Expected invocation on the mock once, but was 0 times: x =>  x.Upload(It.Is<Object>(t => t != null)), Times.Once);

As per my analysis verify method gets called before calling async method. 根据我的分析,在调用异步方法之前调用验证方法。 This can be reason of failure of this test case. 这可能是此测试用例失败的原因。 How can I verify that a method was called on a mock when the method itself is called in a delegate passed to Task.Run? 当在传递给Task.Run的委托中调用方法本身时,如何验证在模拟上调用方法? By time mock.Verify is called the Task still hasn't executed. 按时间mock.Verify被称为任务仍然没有执行。 Could anyone please provide some solution so that this test case will pass each and every time 任何人都可以提供一些解决方案,以便这个测试用例每次都会通过

As others have noted, the best solution is to make the method return Task . 正如其他人所说, 最好的解决方案是让方法返回Task Task -returning async methods are more easily testable: Task返回async方法更容易测试:

private async Task UploadAsync(Object data)
{
  await Task.Run(() =>
  {
    using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
    {
      factory.Repository.UploadData(data);
    }
  });
}

private async void Upload(Object data)
{
  await UploadAsync(data);
}

Then your unit test can be: 然后你的单元测试可以是:

[TestMethod, TestCategory("Unit")]
public async Task Upload_ValidData_Success()
{
  //other logic to setup mock repository
  //call public method which calls async method
  await engine.UploadAsync(data);

  _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}

However, if for some reason you do need to test an async void method, it's possible to do by using my AsyncContext type : 但是,如果由于某种原因您需要测试async void方法,则可以使用我的AsyncContext类型

[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
  //other logic to setup mock repository
  //call public method which calls async method
  AsyncContext.Run(() => engine.Upload(data));

  _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}

Side note: In an ideal scenario, I'd recommend adding an UploadDataAsync to your repository, and then remove the Task.Run . 附注:在理想情况下,我建议将UploadDataAsync添加到存储库,然后删除Task.Run

One of the main benefits of unit tests is that they help you design better code. 单元测试的主要好处之一是它们可以帮助您设计更好的代码。 Most of the time, when you are struggling to write a test, it is telling you that your code needs to be improved, not your test. 大多数情况下,当您努力编写测试时,它告诉您需要改进代码,而不是测试。

In this case the issue is using a void return type with an async method, something which Stephen Cleary advises against . 在这种情况下,问题是使用带有async方法的void返回类型,这是Stephen Cleary 建议的 Indeed, one of the reasons he cites in his article is: 实际上,他在文章中引用的原因之一是:

Async void methods are difficult to test. 异步void方法很难测试。

I think one of his most convincing arguments against async void methods is this: 我认为他对async void方法的一个最有说服力的论点是这样的:

When the return type is Task, the caller knows it's dealing with a future operation; 当返回类型为Task时,调用者知道它正在处理将来的操作; when the return type is void, the caller might assume the method is complete by the time it returns. 当返回类型为void时,调用者可能会认为该方法在返回时已完成。

In case you're not convinced yet, here's another article which contains a couple of other reasons to avoid async void . 如果您还不相信, 这里有另一篇文章 ,其中包含避免async void的其他几个原因。

In summary, you should change your method to return a Task . 总之,您应该更改方法以返回Task Then you will be able to wait on it in the test. 然后你就可以在测试中等待它了。

private async Task UploadAsync(Object data)
{
    return Task.Run(() => {
           using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
           {
               factory.Repository.UploadData(data);
           }
    });
}

and the test... 和测试......

[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
    //other logic to setup mock repository
    //call public method which calls async method
    engine.Upload(data).Wait();

   _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM