如果我尝试模拟包含以下async
方法的类型:
interface Foo
{
Task<int> Bar();
}
然后模拟的Bar
方法返回null。我猜 Moq 选择default(Task<int>)
作为我的方法的默认返回值,这确实是null
. 然而,起订量应该选择类似的东西Task.FromResult(default(int))
作为默认值。我可以强制 Moq 使异步方法返回非空任务吗?
如果我尝试模拟包含以下async
方法的类型:
interface Foo
{
Task<int> Bar();
}
然后模拟的Bar
方法返回null。我猜 Moq 选择default(Task<int>)
作为我的方法的默认返回值,这确实是null
. 然而,起订量应该选择类似的东西Task.FromResult(default(int))
作为默认值。我可以强制 Moq 使异步方法返回非空任务吗?
如果有人感兴趣,我创建了一个扩展类,它使异步方法的存根不那么冗长:
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 { }));
}
}
一些例子 :
//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;
您只需要存根 Bar 方法,并使其返回Task.FromResult(default(int))
看起来这个问题在 Moq 4.2 中已修复。所以你只需要升级到最新版本的起订量(至少在我的情况下它开始返回非空任务)
回想一下,Moq 框架是开源的。在代码库(可在此处获得)中,我们可以看到,当执行尚未设置的方法调用时,返回值是类中valueDel
私有字段 的结果MethodCallReturn<TMock, TResult>
。该字段被实例化,以便它返回结果类型的默认值:
private Delegate valueDel = (Func<TResult>)(() => default(TResult));
您可以添加一个方法来覆盖从 Mock 返回的给定类型的默认值,或者在这种Task
情况下显式返回您建议的默认值。
您也可以在起订量问题列表页面上提出问题。
但是,在不处理 Moq 源的情况下,您只需要Foo
像 aquaraga 建议的那样对接口进行存根。可以在此处找到对模拟和存根之间区别的快速解释。