2017-09-26 99 views
-1

内等待我有此代码与异步测试方法的任务

public class ClassToTest 
{ 
    private readonly IRepository repository; 

    public ClassToTest(DI GOES HERE){...} 

    public DoSomething() 
    { 
     Task.Run(async() => { 
      //some code 
      repository.ExecuteAsync(); 
     } 
    } 
} 

public class Repository : IRepository 
{ 
    public Task ExecuteAsync() 
    { 
     using (var connection = new SqlConnection(DbConfiguration.DatabaseConnection)) 
     { 
      return connection.ExecuteAsync(storedProcedure, parameters, commandType: CommandType.StoredProcedure, commandTimeout: Configuration.TransactionTimeout); 
     } 
    } 
} 

[Test] 
public void TestMethod() 
{ 
    var repository = new Mock<IRepository>; 
    var classToTest = new ClassToTest(); 

    classToTest.DoSomething(); 

    repository.Veryfy(p => p.ExecuteAsync(), Times.Once()); 
} 

测试失败,此消息

上模拟预期调用一次,但是0次:P =>页。 ExecuteAsync()

有谁知道为什么?

感谢

+3

我怀疑“DI GOES HERE”可能会持有什么回事 – doctorlove

+4

一些线索由于'DoSomething'其实异步不该”你等待该方法完成? – SteppingRazor

+0

测试需要是异步的,模拟需要在被调用时设置为异步。 – Nkosi

回答

5

正如其他人所提到的,因为你打电话Task.Run,而不是等待响应,单元测试可能会完成之前的后台任务甚至开始,因此Moq验证失败。

此外,您的代码不会按原样编译 - 在StackOverflow上询问Q时,请务必提供完整的可编译MVP。

特别重要的是您试图测试的代码中的错误。 Repository.ExecuteAsync调用connection.ExecuteAsync,在using范围内,但这并不等待。这将意味着连接将be disposed before the task completes。您需要将方法更改为asyncawait推迟处理连接的呼叫。

的包装方法DoSomething方法不应该使用Task.Run(),虽然,因为它adds no value到存储库任务,它并不需要重复async/return await,无论是。

主叫方(单元测试,在这种情况下),则可以等待DoSomething(或者如果主叫方真正想要做进一步的处理,而不等待任务,然后把它留给调用者来决定,至少这样,调用者获得该任务的句柄,以检查完成)。

代码的最终状态看起来更像是这样的:

public class ClassToTest 
{ 
    private readonly IRepository _repository; 

    public ClassToTest(IRepository repository) 
    { 
     _repository = repository; 
    } 

    // Doesn't necessarily need to be async 
    public Task DoSomething() 
    { 
     // We're return the wrapped task directly, and adding no additional value. 
     return repository.ExecuteAsync(); 
    } 
} 

public class Repository : IRepository 
{ 
    public async Task ExecuteAsync() 
    { 
     using (var connection = new SqlConnection(DbConfiguration.DatabaseConnection)) 
     { 
      // Here we do need to await, otherwise we'll dispose the connection 
      return await connection.ExecuteAsync(storedProcedure, parameters, 
       commandType: CommandType.StoredProcedure, 
       commandTimeout: Configuration.TransactionTimeout); 
     } 
    } 
} 

// NUnit has full support for async/await 
[Test] 
public async Task TestMethod() 
{ 
    var repository = new Mock<IRepository>(); 
    var classToTest = new ClassToTest(repository.Object); 

    repository.Setup(_ => _.ExecuteAsync()).Returns(Task.FromResult((object)null)); 
    // Moq also has support for async, e.g. .ReturnsAsync 

    // You need to await the test. 
    await classToTest.DoSomething(); 

    repository.Verify(p => p.ExecuteAsync(), Times.Once()); 
} 
+0

也进行了另一次编辑,因为您必须设置模拟以返回已完成的任务,以允许异步代码完成。 – Nkosi

+0

除非需要延迟执行lambda超载,否则您还可以使用'.ReturnsAsync(...)'而不是被黑的'Task.FromResult'。 – StuartLC

+0

在哪个Moq版本中'ReturnsAsync'不带任何参数?我一直在等待那一段时间。 – Nkosi