在VS15中使用Moq进行单元测试时,returnsasync(null)会产生构建错误

时间:2017-01-13 13:48:26

标签: c# unit-testing asynchronous moq

当我在Visual Studio中的C#单元测试方法中使用ReturnsAsync(null)时(使用Moq),我收到错误:

  

“以下方法或属性之间的调用不明确”

然后是具有不同参数的ReturnsAsync方法列表。我知道这是由于ReturnsAsync函数被重载。但是,当我在同事的计算机上运行相同的单元测试时,它运行没有任何错误。有谁知道为什么会这样?有谁知道如何解决这个问题?

另外,当我建立时,我收到警告:

  

所有引用********的软件包都必须安装nuget软件包Microsoft.Bcl.Build。

这会有什么影响吗?

3 个答案:

答案 0 :(得分:25)

Moq ReturnsAsync类中有两个ReturnsExtensions扩展方法。它们具有以下参数:

(this IReturns<TMock, Task<TResult>> mock, TResult value)
(this IReturns<TMock, Task<TResult>> mock, Func<TResult> valueFunction)

正如您所见,一个接受应该由task返回的值,另一个接受将返回值的委托。当您通过null编译器时,不知道它是值还是委托。当task参数是值类型(例如int)时,情况并非如此。因为它不能为null并且编译器理解null是委托。可能与您同事的计算机有关。

要修复此错误,您需要帮助编译器选择正确的方法重载 - 将null转换为任务类型的结果(例如字符串):

RetursAsync((string)null)

或者你可以传递null的值

string s = null;
... ReturnsAsync(s);

答案 1 :(得分:1)

问题是编译器正在根据其参数类型和传递的参数类型来选择调用哪个重载方法。这称为方法重载解析。但是 literal null 表示“没有值”,同时,它本身并不携带类型信息。如果没有类型,编译器不知道要调用哪个重载方法,因此它会抱怨“调用不明确”。

当你有文字 "Hello world" 时,你(编译器)知道它有一个值(文本“Hello world”)并且它的类型是字符串。但在 null 的情况下,类型信息丢失 - 它可能是没有值的字符串、没有值的委托或例如没有值的自定义引用类型。

正如 Sergey 提到的,有两种重载方法:

(this IReturns<TMock, Task<TResult>> mock, TResult value)
(this IReturns<TMock, Task<TResult>> mock, Func<TResult> valueFunction)

它们都有一个参数,第一个参数为 TResult 类型,第二个参数为 Func<TResult> 类型。没有类型信息的 null 可能是两者/任何一个,所以你只需要提示编译器使用哪个。

您有两个选择:

  • null 值链接到某种类型并调用接受 TResult 的重载
  • 传递一个返回null的函数,即调用接受Func<TResult>的重载

对我来说,最简单的解决方案是将 null 包装成一个函数,这样我就不必再担心类型了。我只是传递了一个返回 nullexpression lambda 作为参数,这意味着我使用了第二个接受委托 Func<TResult> 的重载方法:

ReturnAsync(() => null)

另一方面,正如 Sergey 提到的,您可以将 null 分配给一个类型化变量并传递它:

string x = null;
‪….ReturnAsync(x);

这样,您已将 null 与特定类型(在本例中为字符串)链接,因此编译器将再次知道要使用哪个重载。

答案 2 :(得分:1)

示例如何显式传递值

GetTokenClient.Setup(e =>
    e.GetToken(It.IsAny<GetTokenParams>(),It.IsAny<CancellationToken>())
).ReturnsAsync(
    (GetTokenParams GetTokenParams, CancellationToken CancellationToken) => 
        new BaseResult<GetTokenResult>() { IsSuccess = true }
);