如何通过提供方法名称作为字符串来模拟服务方法

时间:2019-06-03 13:28:01

标签: c# moq

我想通过将服务名称提供为字符串来模拟服务方法:

public interface IService 
{
    SomeType SomeMethod(int a, int b);
}

...
var service = new Mock<IService>();
string methodName = nameof(IService.SomeMethod);

service.Setup(s => s."methodName"(It.IsAny<int>(), It.IsAny<int>())
    .Callback<int, int>(...faking is not related);

我知道我必须以某种方式使用Expression,但是我无法想象如何基于IService和It.IsAny参数来创建此表达式。

1 个答案:

答案 0 :(得分:2)

您可以构造一个表达式,例如如下:

    private static Expression<Action<T>> ConstructMethodCallExpressionWithItIsAnyOnAllArguments<T>(string mehodName)
    {
        var methodInfo = typeof(T).GetMethod(mehodName);

        var argumentExpression = Expression.Parameter(typeof(T));

        var methodExpression = Expression.Call(
            argumentExpression,
            methodInfo,
            methodInfo.GetParameters().Select(pi => Expression.Call(
                typeof(It), "IsAny", new Type[] { pi.ParameterType }, new Expression[0])));

        var result = Expression.Lambda<Action<T>>(
            methodExpression,
            argumentExpression);
        return result; //E.g.: Expression<Action<IService>>(s => s.SomeMethod(It.IsAny<int>(), It.IsAny<int>())
    }

用法:

    var service = new Mock<IService>();
    string methodName = nameof(IService.SomeMethod);
    service.Setup(ConstructMethodCallExpressionWithItIsAnyOnAllArguments<IService>(methodName))
            .Callback<int, int>((x, y) => { ... });

如果需要Func表达式:

    private static Expression<Func<T, TResult>> ConstructMethodCallExpressionFunc<T, TResult>(string mehodName)
    {
        var methodInfo = typeof(T).GetMethod(mehodName);

        var argumentExpression = Expression.Parameter(typeof(T));

        var methodExpression = Expression.Call(
            argumentExpression,
            methodInfo,
            methodInfo.GetParameters().Select(pi => Expression.Call(
                typeof(It), "IsAny", new Type[] { pi.ParameterType }, new Expression[0])));

        var result = Expression.Lambda<Func<T, TResult>>(
            methodExpression,
            argumentExpression);
        return result; //E.g.: Expression<Func<IService, string>>(s => s.SomeMethod(It.IsAny<int>(), It.IsAny<int>())
    }

使用方式:

    var service = new Mock<IService>();
    string methodName = nameof(IService.SomeMethod);

    service.Setup(ConstructMethodCallExpressionFunc<IService, string>(methodName))
           .Returns<int, int>((x, y) => (x.ToString() + y.ToString()));