用于验证方法的模拟(Moq库)的通用方法永远不会使用任何参数组合调用

时间:2015-11-08 10:36:57

标签: c# unit-testing generics mocking moq

动机:我发现自己经常写这样的代码......

myMock.Verify(m => m.SomeMethod(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<string>(), ..., It.IsAny<int>()), Times.Never());

也就是说,我经常只是想确保给定的方法永远不会被任何参数组合调用。

写出所有这些It.IsAny()常量有点痛苦,我想避免它。我的想法是使用反射来自动化这个常见的用例。这是我第一次尝试一种能够为我做到这一点的方法:

    public void VerifyNeverCalledWithAnyParameters<T>(Mock<T> mock, string methodName) where T : class
    {
        // Get the info for the method in which we're interested
        var methodInfo = typeof(T).GetMethod(methodName);
        // Get the parameter info for that method
        var parameters = methodInfo.GetParameters();
        // Build up a list of parameter expressions. Each one will be an It.IsAny<K>() constant, where K is the type of the respective parameter
        var parameterExpressions = new List<Expression>();
        foreach (var parameterInfo in parameters)
        {
            var parameterType = parameterInfo.ParameterType;
            var itIsAnyObject = typeof(It).GetMethod("IsAny").MakeGenericMethod(parameterType).Invoke(null, null);
            parameterExpressions.Add(Expression.Constant(itIsAnyObject));
        }

        // Build up the lambda which represents "m => m.MethodName(It.IsAny<K1>(), It.IsAny<K2>(),...)"
        var delegateType = typeof(Action<IFoo>);
        var parameter = Expression.Parameter(typeof(IFoo), "f");
        var yourExpression = Expression.Lambda(delegateType, Expression.Call(parameter, methodInfo, parameterExpressions), parameter);

        // Verify the method call by invoking the verify method on the mock with our expression and asserting Times.Never() to ensure it was never called with any parameters
        mock.GetType().GetMethod("Verify", new[] { yourExpression.GetType(), typeof(Times) }).Invoke(mock, new object[] { yourExpression, Times.Never() });
    }

假设我有一个接口public interface IFoo { string MyFoo(int); },我会用VerifyNeverCalledWithAnyParameters(mockFoo, "MyFoo");调用此方法。

虽然它编译,但似乎并没有“起作用”。也就是说,即使我在进行验证之前调用模拟对象上的方法,验证也会成功。

我不确定如何解决这个问题。任何人都可以在我的反射代码中看到问题吗?

我知道关于SO的许多其他问题,谈论验证在各种异国情况下调用方法,但我还没有找到任何解决方案,以通用方式执行它,就像我希望在这里做的那样对于这个常见用例。

1 个答案:

答案 0 :(得分:1)

如果您愿意使用strict mocks,则可以免费获得此行为。在未设置的严格模拟上调用方法(通过Setup())将无法通过测试。

请注意there are trade-offs between loose and strict mocks。松散模拟的好处是减少了测试设置并减少了脆性测试。严格模拟的好处包括更精确地测试您的被测系统及其依赖性,但代价是更多的测试设置。

在您的情况下,如果您愿意使用增加的测试设置,那么他们可以提供您正在寻找的行为。