Методы настройки Moq - PullRequest
       11

Методы настройки Moq

0 голосов
/ 07 сентября 2018

Мне нужно протестировать много похожих методов

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 тестирование будет таким же, кроме имени метода. Для метода 3, кроме имени метода, тип параметра изменяется. Есть ли способ извлечь все это в универсальную вспомогательную функцию и передать тип параметра и метод для тестирования?
Я хотел бы написать что-то вроде этого:

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

Ответы [ 3 ]

0 голосов
/ 07 сентября 2018

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;
}

Как видите, это не очень элегантное решение, и синтаксис использования не совсем такой, как вы указали, однако пример работает, и это было интересное упражнение. Используйте свое собственное суждение, использовать ли его в тестах или нет.

Надеюсь, это поможет.

0 голосов
/ 07 сентября 2018

Мне удалось добиться этого, создав выражения для базы проверки по переданному методу

// 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.

0 голосов
/ 07 сентября 2018

Я думаю, что в C # есть проекты автоматизированного тестирования ... Вы можете попробовать и поместить общий код в [TestSetup], а методы - в [TestMethod]. В Google вы можете проверить следующую ссылку Автоматическое модульное тестирование - почему? что? который? ... Надеюсь, это поможет, и, пожалуйста, пометьте как ответ, если это решило ваш вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...