最小起订量设置方法

时间:2018-09-06 21:28:44

标签: c# moq

我需要测试许多类似的方法

public interface ITest
{
    void Method1(bool readMode, List<int> list);
    void Method2(bool readMode, List<int> list);
    void Method3(bool readMode, List<string> list);
...
}

测试所有方法非常相似:

public void Method1Test()
{
    Mock<ITest> test = new Mock<Itest>();
    test.Setup(x=>x.Method1(It.IsAny<bool>(), It.IsAny<List<int>>()).Verifable();
    // do stuff
    test.Verify(x=>x.Method1(true, It.IsAny<List<int>>()), Times.AtLeastOnce());
    test.Verify(x=>x.Method1(false, It.IsAny<List<int>>()), Times.Never());
    test.Verify(x=>x.Method1(It.IsAny<bool>(), It.Is<List<int>>(y=>y.Count == 0)), Times.Never());
    test.Verify(x=>x.Method1(It.IsAny<bool>(), It.Is<List<int>>(y=>y.Count == 2)), Times.Once());
}

对于Method2的测试将与方法名称相同。对于Method3,除了方法名称外,参数类型也被更改。有什么方法可以将所有这些提取到辅助泛型函数并传递参数类型和方法进行测试?
我想写这样的东西:

public void Method1Test()
{
    TestAnyMethod<int>(x=>x.Method1);
}
public void Method2Test()
{
    TestAnyMethod<int>(x=>x.Method2);
}
public void Method3Test()
{
    TestAnyMethod<string>(x=>x.Method3);
}

2 个答案:

答案 0 :(得分:2)

Moq使用表达式树进行配置,因此您可以通过构建各种Expression<Action<ITest>>实例来生成通用验证规则。只是一个例子,如何在您的特定情况下实现:

public interface ITest
{
    void Method1(bool readMode, List<int> list);
    void Method2(bool readMode, List<int> list);
    void Method3(bool readMode, List<string> list);
}

[Test]
public void Method1Test()
{
    Mock<ITest> test = new Mock<ITest>();

    TestAnyMethod<ITest, int>(test, "Method1");
    TestAnyMethod<ITest, int>(test, "Method2");
    TestAnyMethod<ITest, string>(test, "Method3");

    test.VerifyAll();
}

private void TestAnyMethod<T, TItem>(Mock<ITest> test, string methodName)
{
    // Arrange
    var type = typeof(T);
    var methodInfo = type.GetMethod(methodName);
    test.Setup(Verifiable<TItem>(type, methodInfo)).Verifiable();

    // Act
    // make verifying call via reflection:

    // object.Method#(true, new List<TItem> { .. }))
    methodInfo.Invoke(test.Object, new object[] {true, new List<TItem>{ default(TItem) } });

    // object.Method#(true, new List<TItem> { .. , .. })
    methodInfo.Invoke(test.Object, new object[] {true, new List<TItem> { default(TItem), default(TItem) } });

    // Assert
    test.Verify(VerifyReadMode<TItem>(type, methodInfo, true), Times.AtLeastOnce());
    test.Verify(VerifyReadMode<TItem>(type, methodInfo, false), Times.Never());
    test.Verify(VerifyListCount<TItem>(type, methodInfo, 0), Times.Never());
    test.Verify(VerifyListCount<TItem>(type, methodInfo, 2), Times.Once());
}

/// <summary>
/// Returns x=>x.Method#(It.IsAny`bool`(), It.IsAny`List`int``()
/// </summary>
/// <param name="mockingType">The type that we mock</param>
/// <param name="method">Verifying method</param>
private Expression<Action<ITest>> Verifiable<T>(Type mockingType, MethodInfo method)
{
    var readModeArg = Expression.Call(typeof(It), "IsAny", new []{ typeof(bool) });
    var listArg = Expression.Call(typeof(It), "IsAny", new[] { typeof(List<T>) });

    return Verify(mockingType, method, readModeArg, listArg);
}

/// <summary>
/// Returns x=>x.Method#(<paramref name="readMode"/>, It.IsAny`List`int``()
/// </summary>
/// <param name="mockingType">The type that we mock</param>
/// <param name="method">Verifying method</param>
/// <param name="readMode"></param>
private Expression<Action<ITest>> VerifyReadMode<T>(Type mockingType, MethodInfo method, bool readMode)
{
    var readModeArg = Expression.Constant(readMode);
    var listArg = Expression.Call(typeof(It), "IsAny", new[] { typeof(List<T>) });

    return Verify(mockingType, method, readModeArg, listArg);
}

/// <summary>
/// Returns x=>x.Method#(It.IsAny`bool`(), It.Is`List`int``(y=>y.Count == <paramref name="count"/>)
/// </summary>
/// <param name="mockingType">The type that we mock</param>
/// <param name="method">Verifying method</param>
private Expression<Action<ITest>> VerifyListCount<T>(Type mockingType, MethodInfo method, int count)
{
    var readModeArg = Expression.Call(typeof(It), "IsAny", new[] { typeof(bool) });

    var listPrm = Expression.Parameter(typeof(List<T>), "y");
    var prop = Expression.Property(listPrm, typeof(List<T>), "Count");
    var equal = Expression.Equal(prop, Expression.Constant(count));
    var lambda = Expression.Lambda<Func<List<T>, bool>>(equal, "y", new [] { listPrm });
    var listArg = Expression.Call(typeof(It), "Is", new[] { typeof(List<T>) }, lambda);

    return Verify(mockingType, method, readModeArg, listArg);
}

/// <summary>
/// Returns lambda expression for verifying <paramref name="method"/> with arguments
/// </summary>
/// <param name="mockingType">The type that we mock</param>
/// <param name="method">Verifying method</param>
/// <param name="readModeArg">Expression for verify readMode argument</param>
/// <param name="listArg">Expression for verify list argument</param>
private Expression<Action<ITest>> Verify(Type mockingType, MethodInfo method, Expression readModeArg, Expression listArg)
{
    var prm = Expression.Parameter(mockingType, "x");
    var methodCall = Expression.Call(prm, method, readModeArg, listArg);
    Expression<Action<ITest>> expr = Expression.Lambda<Action<ITest>>(methodCall, "x", new[] { prm });
    return expr;
}

如您所见,这并不是真正的优雅解决方案,用法语法与您提供的语法并不完全相同,但是示例可以正常工作,这是一个有趣的练习。使用您自己的判断,是否在测试中使用它。

希望有帮助。

答案 1 :(得分:0)

我可以通过基于传递的方法创建用于验证的表达式来实现这一目的

// Helper function to get It.IsAny<T>() Expression
public static System.Linq.Expressions.MethodCallExpression IsAny<T>()
{
    return System.Linq.Expressions.Expression.Call(typeof(It).GetMethod("IsAny").MakeGenericMethod(typeof(T)));
}

// Helper function to check conditions for list.Count
public static bool CheckProperties(System.Collections.IList list, int expected)
{
    return list.Count == expected;
}

public void TestMethod1()
{
    var test = Mock.Of<ITest>();
    Mock.Get(test).Setup(x => x.Method1(It.IsAny<bool>(), It.IsAny<List<int>>())).Verifiable();
    //do stuff
    // Method1 passed as parameter
    TestAnyMethod<int>(test, x => x.Method1);
}

public void TestAnyMethod<T>(ITest test, Func<ITest, Action<bool, List<T>>> methodToTest)
{
    var type = typeof(ITest);
    var param = Expression.Parameter(type);
    var method = type.GetMethod(methodToTest(Mock.Of<ITest>()).Method.Name); // Method that will be tested

    //test.Verify(x => x.METHOD(true, It.IsAny<List<T>>()), Times.AtLeastOnce());
    var call = Expression.Call(param, method, Expression.Constant(true), IsAny<List<T>>());
    Mock.Get(test).Verify(Expression.Lambda<Action<ITest>>(call, param),Times.AtLeastOnce());

    //test.Verify(x => x.METHOD(true, It.IsAny<List<T>>()), Times.AtLeastOnce());
    call = Expression.Call(param, method, Expression.Constant(true), IsAny<List<T>>());
    Mock.Get(test).Verify(Expression.Lambda<Action<ITest>>(call, param), Times.AtLeastOnce());

    var propertiesParam = Expression.Parameter(typeof(List<T>));
    var checkPropertiesMethod = GetType().GetMethod("CheckProperties", BindingFlags.Public | BindingFlags.Static);

    // test.Verify(x => x.METHOD(It.IsAny<bool>(), It.Is<List<T>>(y => y.Count == 0)), Times.Never());
    var checPropertiesCall = Expression.Call(Expression.Constant(this), checkPropertiesMethod, propertiesParam, Expression.Constant(0));
    call = Expression.Call(param, method, TestUtils.IsAny<bool>(), TestUtils.Is<EditFieldProperties<T>>(Expression.Lambda<bool>(checPropertiesCall, propertiesParam)));
    Mock.Get(test).Verify(Expression.Lambda<Action<ITest>>(call, param), Times.Never());

    // test.Verify(x => x.METHOD(It.IsAny<bool>(), It.Is<List<int>>(y => y.Count == 2)), Times.Once());
    checPropertiesCall = Expression.Call(Expression.Constant(this), checkPropertiesMethod, propertiesParam, Expression.Constant(2));
    call = Expression.Call(param, method, TestUtils.IsAny<bool>(), TestUtils.Is<EditFieldProperties<T>>(Expression.Lambda<bool>(checPropertiesCall, propertiesParam)));
    Mock.Get(test).Verify(Expression.Lambda<Action<ITest>>(call, param), Times.Once());

}

在我的项目中,我走得更远。我从“做事”中提取了所有内容,并将其作为VerifyAnyMethod传递给Action。这样,我什至不必每次都设置视图。可以在VerifyAnyMethod内部完成。