首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >不使用Mock.Returns()调用Mock.Setup()

不使用Mock.Returns()调用Mock.Setup()
EN

Stack Overflow用户
提问于 2020-03-25 03:50:59
回答 2查看 1.1K关注 0票数 0

在使用Moq时,您通常需要配置模拟以返回特定的值。要指定要返回的内容,在定义方法应该返回的内容之前,必须先通过使用以下结构定义Setup()的例程:

代码语言:javascript
运行
复制
var o = new ObjectToReturn();

myMock.Setup(m => m.MyMethod(It.IsAny<T1>()...It.IsAny<Tn>()))
      .Returns(o);

相反,我希望“对于具有此名称的方法,返回此值”,如下所示:

代码语言:javascript
运行
复制
myMock.Setup(m => m.GetType().GetMethod("MyMethod")).Returns(o);

代码语言:javascript
运行
复制
myMock.Setup("MyMethod").Return(o);

当我根本不关心类型、值或参数的数量时,还有其他方法可以跳过所有方法参数的详细枚举吗?

我知道有一个方法SetReturnsDefault(),但我不想为模拟的所有方法设置默认值。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-03-25 13:15:54

默认值提供程序可能是一个选项。这与SetReturnsDefault不一样,您有更多的控制权。

非常快速的MVP默认值提供程序,如下所示:

代码语言:javascript
运行
复制
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,而且还获得了提供的参数。足以做出明智的决定。

使用以下几个重载方法的接口和相同的返回类型:

代码语言:javascript
运行
复制
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();
}

测试设置看起来就像

代码语言:javascript
运行
复制
[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表达式和所带来的麻烦,如果您确实为某个成员指定了设置,它将使用该设置;默认值提供程序仅用于尚未指定设置的成员。

工作溶液

票数 1
EN

Stack Overflow用户

发布于 2020-03-25 07:48:01

这是可能的。尝试:

代码语言:javascript
运行
复制
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);
    }
}

用法:

代码语言:javascript
运行
复制
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);

挑战将是处理由模拟类型定义的不同类型的方法:

  • 正规法
  • 泛型法
  • 方法过载
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60842761

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档