在使用Moq时,您通常需要配置模拟以返回特定的值。要指定要返回的内容,在定义方法应该返回的内容之前,必须先通过使用以下结构定义Setup()的例程:
var o = new ObjectToReturn();
myMock.Setup(m => m.MyMethod(It.IsAny<T1>()...It.IsAny<Tn>()))
.Returns(o);
相反,我希望“对于具有此名称的方法,返回此值”,如下所示:
myMock.Setup(m => m.GetType().GetMethod("MyMethod")).Returns(o);
或
myMock.Setup("MyMethod").Return(o);
当我根本不关心类型、值或参数的数量时,还有其他方法可以跳过所有方法参数的详细枚举吗?
我知道有一个方法SetReturnsDefault(),但我不想为模拟的所有方法设置默认值。
发布于 2020-03-25 13:15:54
默认值提供程序可能是一个选项。这与SetReturnsDefault
不一样,您有更多的控制权。
非常快速的MVP默认值提供程序,如下所示:
public class SelectiveDefaultValueProvider : DefaultValueProvider
{
private readonly string _methodName;
private readonly object _returns;
public SelectiveDefaultValueProvider(string methodName, object returns)
{
_methodName = methodName;
_returns = returns;
}
protected override object GetDefaultValue(Type type, Mock mock)
{
var lastInvocation = mock.Invocations.Last();
var methodInfo = lastInvocation.Method;
var args = lastInvocation.Arguments;
if (methodInfo.Name.Equals(_methodName))
{
return _returns;
}
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
}
...allows用于在返回值之前注入一些决策。在本例中,我将检查最后一个调用方法名称,如果匹配,则返回指定对象返回。我没有使用args变量,但我包含了它,以表明您不仅获得了上次调用的MethodInfo
,而且还获得了提供的参数。足以做出明智的决定。
使用以下几个重载方法的接口和相同的返回类型:
public class ObjectToReturn
{
public Guid Id { get; set; }
}
public interface IFoo
{
ObjectToReturn MyMethod(int parameter1);
ObjectToReturn MyMethod(string parameter2);
ObjectToReturn MyMethod(int parameter1, int parameter2);
ObjectToReturn AnotherMethod();
int AValueTypeMethod();
}
测试设置看起来就像
[Test]
public void DefaultValueProvider_ForOverloadedMethod_AllOverloadsReturnSameExpectedResult()
{
var objectToReturn = new ObjectToReturn { Id = Guid.NewGuid() };
var mock = new Mock<IFoo> { DefaultValueProvider = new SelectiveDefaultValueProvider(nameof(IFoo.MyMethod), objectToReturn) };
var mocked = mock.Object;
var result1 = mocked.MyMethod(1);
var result2 = mocked.MyMethod(1, 2);
var result3 = mocked.MyMethod("asdf");
var result4 = mocked.AnotherMethod();
var result5 = mocked.AValueTypeMethod();
Assert.Multiple(() =>
{
Assert.That(result1, Is.SameAs(objectToReturn));
Assert.That(result2, Is.SameAs(objectToReturn));
Assert.That(result3, Is.SameAs(objectToReturn));
Assert.That(result4, Is.Null);
Assert.That(result5, Is.TypeOf<int>());
});
}
正如上面提到的MVP,您可以轻松地扩展缺省值提供程序实现,以获取方法/返回值的列表,收紧从string方法名称到MethodInfo
结果(typeof(IFoo).GetMethods().Where(x => x.Name.StartsWith("MyMethod") && x.ReturnType == typeof(ObjectToReturn))
)的接口,等等。
这种方法的好处是,您不必担心构建Setup
表达式和所带来的麻烦,如果您确实为某个成员指定了设置,它将使用该设置;默认值提供程序仅用于尚未指定设置的成员。
发布于 2020-03-25 07:48:01
这是可能的。尝试:
public interface IService
{
int MyMethod1(int a, object b);
int MyMethod2(int a);
int MyMethod2(object b);
}
public static class MyMockExtensions
{
public static ISetup<T, TResult> Setup<T, TResult>(this Mock<T> mock,
string methodName) where T : class
{
var methods = typeof(T).GetMethods()
.Where(
mi => mi.Name == methodName
&& mi.ReturnType == typeof(TResult))
.ToArray();
if (methods.Length == 0)
{
throw new MissingMethodException("No method found.");
}
if (methods.Length > 1)
{
throw new AmbiguousMatchException("Ambiguous methods found.");
}
var method = methods[0];
// Figure out parameters.
var parameters = method.GetParameters()
// It.IsAny<pi.ParameterType>()
.Select(pi => Expression.Call(
typeof(It), nameof(It.IsAny), new[] { pi.ParameterType }));
// arg0 => arg0.MyMethod(It.IsAny<T1>()...It.IsAny<Tn>())
var arg0 = Expression.Parameter(typeof(T), "arg0");
var setupExpression = Expression.Lambda<Func<T, TResult>>(
Expression.Call(arg0, method, parameters), arg0);
return mock.Setup(setupExpression);
}
}
用法:
var mock = new Mock<IService>();
mock.Setup<IService, int>("MyMethod1").Returns(123);
// method1Result = 123
var method1Result = mock.Object.MyMethod1(1, 2);
// This throws AmbiguousMatchException exception as there are two MyMethod3
// both returning an integer.
mock.Setup<IService, int>("MyMethod2").Returns(234);
挑战将是处理由模拟类型定义的不同类型的方法:
https://stackoverflow.com/questions/60842761
复制相似问题