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

14,214

Solution 1

There are two ReturnsAsync extension methods in Moq ReturnsExtensions class.They have following parameters:

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

As you can see one accepts value which should be returned by task, and another accepts delegate which will return value. When you are passing null compiler don't know whether it value or delegate. It's not the case when task parameter is a value type (e.g. int). Because it cannot be null and compiler understands that null is a delegate. Probably that's the case with your colleague's computer.

To fix this error you need to help compiler choose correct method overload - cast null to type of task's result (e.g. string):

RetursAsync((string)null)

Or you can pass value which is null

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

Solution 2

The problem is that the compiler is selecting which overloaded method to call based on the type of its parameters and the type of parameters being passed. It's called method overload resolution. But the literal null means "without a value" and, at the same time, itself, it doesn't carry the type information. And without the type, the compiler doesn't know which overloaded method to call, so it complains that the "call is ambiguous".

When you have the literal "Hello world", you (the compiler) know that it has a value (text "Hello world") and its type is the string. But in case of null, the type information is missing – it may be a string without a value, a delegate without a value or for example a custom reference type without a value.

As Sergey mentioned, there are two overloaded methods:

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

They both have a single parameter, the first one of type TResult and the second one of type Func<TResult>. The null without a type information may be both/any of them, so you just need to hint the compiler which one to use.

You have two options:

  • link the null value to some type and call the overload accepting TResult
  • pass a function returning the null, i.e. call the overload accepting Func<TResult>

For me, the easiest solution was to wrap the null into a function, so I didn't have to worry about types anymore. I simply passed an expression lambda returning null as the parameter, that means I have used the second overloaded method accepting a delegate Func<TResult>:

ReturnAsync(() => null)

On the other side, as Sergey mentioned, you can assign the null into a typed variable and pass that:

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

That way, you have linked the null with a particular type, a string in this case, so again, the compiler will know which overload to use.

Solution 3

Example how to pass value explicitly

GetTokenClient.Setup(e =>
    e.GetToken(It.IsAny<GetTokenParams>(),It.IsAny<CancellationToken>())
).ReturnsAsync(
    (GetTokenParams GetTokenParams, CancellationToken CancellationToken) => 
        new BaseResult<GetTokenResult>() { IsSuccess = true }
);
Share:
14,214
Justin Borromeo
Author by

Justin Borromeo

Updated on June 17, 2022

Comments

  • Justin Borromeo
    Justin Borromeo almost 2 years

    When I use ReturnsAsync(null) in a C# unit test method in Visual Studio (with Moq), I get the error:

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

    and then a list of the ReturnsAsync methods which have different parameters. I understand that this is due to the ReturnsAsync function being overloaded. However, when I run the same unit test on my colleague's computer, it runs without any errors. Does anyone know why this would happen? Does anyone know how to fix this?

    Also, when I build, I get warnings that:

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

    Could that have any effect?

  • Christopher Scott
    Christopher Scott about 7 years
    alternatively you could pass RetursAsync(default(string))
  • Sven Grosen
    Sven Grosen over 6 years
    Or, if you have lots of these errors after an upgrade and don't want to bother casting to different return types: ReturnsAsync(() => null)