Тестовые дубли (макеты / заглушки) против цепочки методов или синтаксиса свободного интерфейса

У меня есть тестируемый код, который в основном выглядит следующим образом (конкретный код не важен для вопроса. Это просто здесь для пояснительных целей):

public ICollection<Product> GetByCategory(string category, ISession session)
{
    return session
        .CreateCriteria(typeof(Product))
        .Add(Restrictions.Eq("Category", category))
        .List<Product>();
}

Здесь используется цепочка методов (и решение, которое я ищу, также применимо к синтаксису свободного интерфейса ).

Меня не интересует поиск решений только для этого конкретного примера, меня интересует решение более общей проблемы. В этом примере я хотел бы добавить только ожидание для CreateCriteria. Если я сделаю это, то получу исключение NullReferenceException, даже если CreateCriteria вернет заглушку, потому что метод Add возвращает значение null.

Я бы хотел, чтобы мой тест продолжал работать, даже если дополнительные методы связаны или метод Add будет удален.

Есть ли общий прием для уменьшения количества тестовых двойных / ожидаемых вызовов до тех, против которых я хочу утверждать, при использовании цепочки методов?

Решение, которое я могу придумать, - это создать шаблон T4, который перечисляет все методы для типа и создает заглушку с ожиданиями, которые дают разные возвращаемые значения по умолчанию. Но мне интересно, есть ли варианты попроще.

Я использую Rhino.Mocks, но мне хотелось бы получить более общее решение.


person Merlyn Morgan-Graham    schedule 03.09.2010    source источник
comment
@ 01: Я откатил правку, добавившую тег NHibernate. Я использовал NHibernate в качестве примера, но мне особенно нужны решения, которые будут применяться не только к NHibernate. Так что проблема с использованием конкретного примера. Я видел свободный синтаксис в других местах, а также использовал его в частных базах кода.   -  person Merlyn Morgan-Graham    schedule 04.09.2010


Ответы (2)


Возможный подход - обернуть фиктивный объект в DynamicProxy, который всегда возвращает this для методов, которые являются частью свободного API и не имеют записанных ожиданий. Он делегирует обычному фиктивному объекту методы, для которых были записаны ожидания (или которые не являются частью свободного интерфейса).

Определение того, какие методы имеют определенные ожидания, конечно, будет сильно зависеть от MockLibrary. Методы, не владеющие беглым языком, можно легко проверить на предмет самоанализа.

Может, в одной из библиотек это уже встроено?

person Peter Tillemans    schedule 03.09.2010
comment
Библиотеки макетов работают с объектами с виртуальными функциями, поэтому вы можете обернуть объект или интерфейс DynamicProxy, а не макет объекта, возвращаемого из библиотеки, и использовать макет, который делегирует базовую реализацию, когда ожидание не установлено. Если, конечно, такая опция доступна в библиотеке имитаторов. - person Merlyn Morgan-Graham; 04.09.2010

Мне нужно было что-то подобное для интерфейса NHibernate IQuery. Я использовал Castle.DymanicProxy и Rhino.Mocks со следующей реализацией Fake IRepository ...

internal class FakeRepository<TTypeOfModel> : IRepository<TTypeOfModel>
{
    ....

    public IQuery GetNamedQuery(string queryName)
    {
        return MockFactory.MockWithMethodChainingFor<IQuery>();
    }

    ....
}

internal static class MockFactory
{
    public static T MockWithMethodChainingFor<T>()
        where T : class
    {
        var generator = new ProxyGenerator();

        return generator.CreateInterfaceProxyWithTargetInterface(
            MockRepository.GenerateMock<T>(),
            new MethodChainingMockInterceptor<T>());
    }
}

internal class MethodChainingMockInterceptor<T> : StandardInterceptor
{
    protected override void PerformProceed(IInvocation invocation)
    {
        if ((Type)invocation.Method.ReturnType == typeof(T))
        {
            invocation.ReturnValue = invocation.Proxy;
        }
        else
        {
            base.PerformProceed(invocation);
        }
    }
}
person Andy McCluggage    schedule 10.08.2011
comment
+1 за образец кода. Это был один из тех сценариев, для которых мне это нужно. Когда я вернусь к этому типу тестирования, если это сработает, я приму ваш ответ. - person Merlyn Morgan-Graham; 10.08.2011