根据方法链接或流畅的界面语法测试双打(模拟/存根)

时间:2010-09-03 19:01:29

标签: mocking fluent-interface method-chaining

我的测试代码基本上看起来像这样(具体代码对问题并不重要。这只是出于解释目的):

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

这使用方法链(我正在寻找的解决方案也适用于fluent interface syntax)。

我对找到这个具体例子的解决方案不感兴趣,我有兴趣解决一个更普遍的问题。在这个例子中,我只想添加对CreateCriteria的期望。但是,如果我这样做,我会得到一个NullReferenceException,即使我让CreateCriteria返回一个存根,因为Add方法返回null。

我希望我的测试能够继续工作,即使链接了其他方法,或者删除了Add方法。

在使用方法链时,是否有一个通用技巧可以将测试双打/预期调用的数量减少到我想要断言的那些?

我能想到的一个解决方案是创建一个枚举类型上所有方法的T4模板,并创建一个期望的存根,提供不同的默认返回值。但我想知道是否有更简单的选择。

我正在使用Rhino.Mocks,但一般的解决方案会更受欢迎。

2 个答案:

答案 0 :(得分:1)

一种可能的方法是将模拟对象包装在DynamicProxy中,该方法总是为方法返回this,这些方法是流畅的API的一部分,没有记录的预期。它委托给普通的模拟对象,用于记录期望的方法(或者不是流畅界面的一部分)。

检测哪些方法具有预期定义当然将取决于MockLibrary。使用内省可以轻松测试非流畅的方法。

也许其中一个图书馆已经建成了这个?

答案 1 :(得分:1)

对于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);
        }
    }
}