Mock 的通用方法(Moq 库)验证从未使用任何参数组合调用方法

Generic Method for Mock (Moq library) to verify a method was NEVER called with ANY parameter combination

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

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");.

调用这个方法

虽然它编译了,但似乎没有"work"。也就是说,即使我在验证之前调用模拟对象上的方法,验证也会成功。

我不确定如何继续解决这个问题。有人能看出我的反射代码有问题吗?

我知道关于 SO 的许多其他问题都在谈论验证方法是否在各种奇特的场景中被调用,但我还没有找到任何解决方案,可以像我希望的那样以通用的方式做到这一点在这里为这个常见的用例做。

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

注意there are trade-offs between loose and strict mocks。松散模拟的好处是减少测试设置和减少测试的脆弱性。严格模拟的好处包括以更多的测试设置为代价对被测系统及其依赖项进行更精确的测试。

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