Reputation: 1485
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 ?
Upvotes: 19
Views: 11285
Reputation: 1485
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;
Upvotes: 34
Reputation: 1891
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)
Upvotes: 4
Reputation: 16324
Recall that the Moq framework is open source. In the code base (available here), we can see that when a method call that has not been set up is executed, the return value is the result of the valueDel
private field in the MethodCallReturn<TMock, TResult>
class. That field is instantiated so that it returns the default value of the result type:
private Delegate valueDel = (Func<TResult>)(() => default(TResult));
You could add a method that would override the default value for a given type that is being returned from a Mock, or explicitly return your suggested default in the Task
case.
You could also file an issue on the Moq issues list page.
Without dealing with the Moq source, though, you'll have to just stub the Foo
interface, as aquaraga suggested. A quick explanation of the difference between mocks and stubs can be found here.
Upvotes: 2
Reputation: 4168
You will just have to stub the Bar method, and make it return Task.FromResult(default(int))
Upvotes: 6