Как проверить лямбда-функции с помощью Moq? - PullRequest
4 голосов
/ 20 сентября 2010

Есть функция:

public class MyCacheClass : ICache
{
    public void T GetObject<T>(Func<T> func)
    {
        T t;
        ...
        t = func();
        ...
        return t;
    }
}

public class MyWorkClass : IWork
{
    public Object MyWorkMethod(string value)
    {
        return new object();
    }
}

Эти функции были вызваны следующим образом:

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        ...
        Object obj = cache(()=>work.MyWorkMethod(strVal));
        ...
    }
}

Для этого необходимо написать UnitTest (с Moq) и проверить, что соответствующий параметр передается в MaCacheClass.GetObject.

Это должно быть примерно так:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(...)).Times.Once());
}

Как я могу предоставить параметр, который будет соответствовать «лямбда-функции»? Есть ли другие варианты?

Любые мысли приветствуются.

Ответы [ 2 ]

1 голос
/ 21 сентября 2010

Вы можете изменить класс так, чтобы вы передавали лямбда в класс, а не весь IWorkClass. Тогда вы сможете хранить и проверять эту фактическую лямбду.

В качестве альтернативы вы можете использовать механизм обратного вызова для фактического вызова лямбды, которая передается в кэш, а затем проверить, что вместо этого макета был вызван IWorkClass.MyWorkMethod. Это соответствует тому, как кэш и рабочий класс в конечном итоге используются, когда кеш является ценным, поэтому он также может предоставить лучший пример ценного поведения, чем просто тестирование метода в отдельности.

В-третьих, вы можете просто использовать It.IsAny<Func<string, void>>() и согласиться с тем, что вам придется правильно проверять этот бит (до тех пор, пока его легче понять, чем неправильно, обычно это нормально). Я обычно называю своих делегатов или лямбда-сигнатур, чтобы, возможно, я неправильно понял эту вещь. Это сработает.

В качестве четвертой альтернативы разверните небольшую заглушку кеша, вместо того чтобы использовать фальшивый фреймворк, затем возьмите и вызовите лямбду из этого. Несмотря на то, что мне нравится Moq и его аналог Java Mockito, иногда я нахожу, что юнит-тесты гораздо проще читать и поддерживать, когда мы просто признаем, что инструменты не поддерживают то, что мы пытаемся с ними делать. Это всего лишь несколько строк кода, и есть вероятность, что кэш-заглушка будет полезен и для других модульных тестов.

1 голос
/ 20 сентября 2010

Один из вариантов может быть следующим: вместо параметра Funct передать некоторый объект, который реализует интерфейс с функцией 'GetObject', например:

public interface IGetObject
{
    T GetObject<T>();
}

public class MyGetObject : IGetObject
{
    public MyGetObject()
    {
    }

    public void Init(string strVal, Func<string, T> func)
    {
        _strVal = strVal;
        _func = func;
    }

    public T GetObject<T>()
    {
        return _func(_strVal);
    }
}

public class MyCacheClass : ICache
{
    public void T GetObject<T>(IGetObject getter)
    {
        T t;
        ...
        t = getter.GetObject<T>();
        ...
        return t;
    }
}

public class MyTestableClass 
{
    public void MyTestableFunc(ICache cache, IWorkClass work)
    {
        string strVal="...";
        IGetObject getter = UnityContainer.Resolve<IGetObject>();
        getter.Init(strVal, str=>work.MyWorkMethod(str));
        ...
        Object obj = cache.GetObject(getter);
        ...
    }
}

В этом случае, я думаю, нам также нужно ввестиUnityContainer в тестируемый объект, поэтому он будет выглядеть примерно так:

[TestMethod]
public void MyTest()
{
    Mock<ICache> mockCache = new Mock<ICache>();
    Mock<IWorkClass> mockWorkClass  = new Mock<IWorkClass>();
    Mock<IGetObject> mockGetter  = new Mock<IGetObject>();

    mockGetter.Setup(mock=>mock.GetObject()).Return(new Object());

    MyTestableClass testable = new MyTestableClass();
    testable.MyTestableFunc(mockCache.Object, mockWorkClass.Object);

    // should I check if 'MyCacheClass' was called with proper parameter?
    mockCache.Verify(mock=>mock.GetObject(mockGetter.Object)).Times.Once());
}

И предоставит дополнительный тест для метода GetObject MyCacheClass, который проверит, вызывает ли он метод GetObject параметра IGetObject...

PS Немного сложное решение ... поэтому, если кто-нибудь увидит лучшее, пожалуйста, сообщите!

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