Returnssasync(null) 在 VS15 中使用 Moq 进行单元测试时创建构建错误

Returnsasync(null) creates a build error when using Moq for unit testing in VS15

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

"The call is ambiguous between the following methods or properties"

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

此外,当我构建时,我收到以下警告:

all packages referencing ******** must install nuget package Microsoft.Bcl.Build.

这有什么影响吗?

Moq 中有两个 ReturnsAsync 扩展方法 ReturnsExtensions class.They 具有以下参数:

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

如您所见,一个接受应由任务 return 编辑的值,另一个接受将 return 值的委托。当您传递 null 时,编译器不知道它是值还是委托。当任务参数是值类型(例如 int)时,情况并非如此。因为它不能为 null 并且编译器知道 null 是一个委托。你同事的电脑可能就是这样。

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

RetursAsync((string)null)

或者你可以传递一个空值

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

问题是编译器根据其参数类型和传递的参数类型选择要调用的重载方法。它被称为方法重载解析。但是 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 可能是其中的 both/any 个,因此您只需要提示编译器使用哪一个即可。

你有两个选择:

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

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

ReturnAsync(() => null)

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

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

这样,您就link编辑了具有特定类型的 null,在本例中为字符串,因此编译器将再次知道要使用哪个重载。

示例如何显式传递值

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