我有一个接口声明

Task DoSomethingAsync();

我正在使用MoqFramework进行测试:

[TestMethod()]
public async Task MyAsyncTest()
{
   Mock<ISomeInterface> mock = new Mock<ISomeInterface>();
   mock.Setup(arg => arg.DoSomethingAsync()).Callback(() => { <my code here> });
   ...
}

然后在我的测试中,执行调用await DoSomethingAsync()的代码。测试在这一行就失败了。我做错了什么?


当前回答

你的方法没有任何回调,所以没有理由使用. callback()。您可以使用. returns()和Task简单地返回一个带有所需值的Task。FromResult,例如:

MyType someValue=...;
mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.FromResult(someValue));

更新2014-06-22

Moq 4.2有两个新的扩展方法来帮助实现这一点。

mock.Setup(arg=>arg.DoSomethingAsync())
    .ReturnsAsync(someValue);

mock.Setup(arg=>arg.DoSomethingAsync())        
    .ThrowsAsync(new InvalidOperationException());

更新2016-05-05

正如Seth Flowers在另一个回答中提到的,ReturnsAsync仅适用于返回Task<T>的方法。对于只返回Task的方法,

.Returns(Task.FromResult(default(object)))

可以使用。

正如这个答案所示,在。net 4.6中,这被简化为. returns (Task.CompletedTask);,例如:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);

其他回答

你只需要添加.Returns(Task.FromResult(0));在回调之后。

例子:

mock.Setup(arg => arg.DoSomethingAsync())
    .Callback(() => { <my code here> })
    .Returns(Task.FromResult(0));

感谢公认的答案,非常有用的解决方案。

我已经为我们的项目添加了两个扩展方法(Task和ValueTask),以使它更容易被习惯在所有异步设置上调用. returnsasync的人发现。

public static class MoqExtensions
{
    public static IReturnsResult<TMock> ReturnsAsync<TMock>(this IReturns<TMock, Task> mock)
        where TMock : class
    {
        return mock.Returns(Task.CompletedTask);
    }

    public static IReturnsResult<TMock> ReturnsAsync<TMock>(this IReturns<TMock, ValueTask> mock) 
        where TMock : class
    {
        return mock.Returns(ValueTask.CompletedTask);
    }
}

可以这样调用:

mock.Setup(x => x.DoSomethingAsync())        
    .ReturnsAsync();

类似的问题

我有一个大致看起来像这样的界面:

Task DoSomething(int arg);

症状

我的单元测试失败时,我的服务在测试等待调用DoSomething。

Fix

与接受的答案不同,在此场景中,您无法在此方法的Setup()上调用. returnsasync(),因为该方法返回非泛型Task,而不是Task<T>。

然而,你仍然可以在设置中使用. returns (Task.FromResult(default(object)),允许测试通过。

现在你也可以使用Talentsoft.Moq.SetupAsync包 https://github.com/TalentSoft/Moq.SetupAsync

根据这里找到的答案和Moq提出的想法,但还没有在这里实现:https://github.com/moq/moq4/issues/384,大大简化了异步方法的设置

在SetupAsync扩展之前的响应中发现的几个例子:

mock.SetupAsync(arg=>arg.DoSomethingAsync());
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Callback(() => { <my code here> });
mock.SetupAsync(arg=>arg.DoSomethingAsync()).Throws(new InvalidOperationException());

你的方法没有任何回调,所以没有理由使用. callback()。您可以使用. returns()和Task简单地返回一个带有所需值的Task。FromResult,例如:

MyType someValue=...;
mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.FromResult(someValue));

更新2014-06-22

Moq 4.2有两个新的扩展方法来帮助实现这一点。

mock.Setup(arg=>arg.DoSomethingAsync())
    .ReturnsAsync(someValue);

mock.Setup(arg=>arg.DoSomethingAsync())        
    .ThrowsAsync(new InvalidOperationException());

更新2016-05-05

正如Seth Flowers在另一个回答中提到的,ReturnsAsync仅适用于返回Task<T>的方法。对于只返回Task的方法,

.Returns(Task.FromResult(default(object)))

可以使用。

正如这个答案所示,在。net 4.6中,这被简化为. returns (Task.CompletedTask);,例如:

mock.Setup(arg=>arg.DoSomethingAsync())        
    .Returns(Task.CompletedTask);