If I try to mock a type containing an async
method such as :
interface Foo
{
Task<int> Bar();
}
Then the mock's Bar
method is returning null. I guess Moq is choosing default(Task<int>)
as default return value for my method, which is indeed null
. However Moq should rather choose something like Task.FromResult(default(int))
as default value. Can I force Moq to make async methods returning non-null Tasks ?
The first version works because null is a valid result for an expected result of a void method, or no result value from the method or assignment to a variable. For the async code the same overall logic applies - JsEditorInterop returns null and SetFocus() is never called.
First, you should never return a null Task . In the async world, a null task just doesn't make sense. Task represents the execution of the asynchronous method, so for an asynchronous method to return a null task is like telling the calling code "you didn't really just call this method" when of course it did.
Async methods can have the following return types: Task, for an async method that performs an operation but returns no value. Task<TResult>, for an async method that returns a value. void , for an event handler.
In short, if your async method is an event handler or a callback, it's ok to return void .
If someone is interested, I made an extension class which makes async methods stubing less verbose :
public static class SetupExtensions
{
public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
this ISetup<TMock, Task<TResult>> setup) where TMock : class
{
return setup.Returns(() => Task.FromResult(default(TResult)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
this ISetup<TMock, Task<TResult>> setup, TResult value) where TMock : class
{
return setup.Returns(() => Task.FromResult(value));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, TResult>(
this ISetup<TMock, Task<TResult>> setup, Func<TResult> func) where TMock : class
{
return setup.Returns(Task.Factory.StartNew(func));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T, TResult>(
this ISetup<TMock, Task<TResult>> setup, Func<T, TResult> func) where TMock : class
{
return setup.Returns<T>(arg => Task.Factory.StartNew(() => func(arg)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, TResult>(
this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, TResult> func) where TMock : class
{
return setup.Returns<T1, T2>((arg1, arg2) => Task.Factory.StartNew(() => func(arg1, arg2)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, TResult>(
this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, T3, TResult> func) where TMock : class
{
return setup.Returns<T1, T2, T3>((arg1, arg2, arg3) => Task.Factory.StartNew(() => func(arg1, arg2, arg3)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, T4, TResult>(
this ISetup<TMock, Task<TResult>> setup, Func<T1, T2, T3, T4, TResult> func) where TMock : class
{
return setup.Returns<T1, T2, T3, T4>((arg1, arg2, arg3, arg4) => Task.Factory.StartNew(() => func(arg1, arg2, arg3, arg4)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock>(this ISetup<TMock, Task> setup, Action action) where TMock : class
{
return setup.Returns(Task.Factory.StartNew(action));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T>(this ISetup<TMock, Task> setup, Action<T> action) where TMock : class
{
return setup.Returns<T>(arg => Task.Factory.StartNew(() => action(arg)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2>(this ISetup<TMock, Task> setup, Action<T1, T2> action) where TMock : class
{
return setup.Returns<T1, T2>((arg1, arg2) => Task.Factory.StartNew(() => action(arg1, arg2)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3>(this ISetup<TMock, Task> setup, Action<T1, T2, T3> action) where TMock : class
{
return setup.Returns<T1, T2, T3>((arg1, arg2, arg3) => Task.Factory.StartNew(() => action(arg1, arg2, arg3)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock, T1, T2, T3, T4>(this ISetup<TMock, Task> setup, Action<T1, T2, T3, T4> action) where TMock : class
{
return setup.Returns<T1, T2, T3, T4>((arg1, arg2, arg3, arg4) => Task.Factory.StartNew(() => action(arg1, arg2, arg3, arg4)));
}
public static IReturnsResult<TMock> ReturnsTask<TMock>(this ISetup<TMock, Task> setup) where TMock : class
{
return setup.Returns(Task.Factory.StartNew(delegate { }));
}
}
Some examples :
//Example 1 :
public interface IFoo
{
Task Bar();
}
var mock = new Mock<IFoo>();
mock.Setup(m => m.Bar()).ReturnsTask(); //await Bar() will return void
//Example 2 :
public interface IFoo
{
Task<int> Bar();
}
var mock = new Mock<IFoo>();
mock.Setup(m => m.Bar()).ReturnsTask(); //await Bar() will return default(int)
//Example 3 :
public interface IFoo
{
Task<int> Bar();
}
var mock = new Mock<IFoo>();
mock.Setup(m => m.Bar()).ReturnsTask(4); //await Bar() will return 4;
//Example 4 :
public interface IFoo
{
Task<int> Bar(int x, int y);
}
var mock = new Mock<IFoo>();
mock.Setup(m => m.Bar(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsTask<IFoo, int, int, int>((x,y) => x + y); //await Bar(x, y) will return x + y;
You will just have to stub the Bar method, and make it return Task.FromResult(default(int))
Looks like this problem is fixed in Moq 4.2. So you just need to upgrade to the newest version of Moq (at least it started returning non-empty Tasks in my case)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With