代码之家  ›  专栏  ›  技术社区  ›  Merlyn Morgan-Graham

针对方法链接或Fluent接口语法测试双精度(mock/stubs)

  •  2
  • Merlyn Morgan-Graham  · 技术社区  · 14 年前

    我测试的代码基本上是这样的(具体的代码对问题不重要)。此处仅作说明之用):

    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,即使我让createCreateCriteria返回一个存根,因为add方法返回空值。

    我希望我的测试继续工作,即使附加方法被链接,或者添加方法被删除。

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

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

    我用的是犀牛。嘲笑,但一般的解决方案会更受欢迎。

    2 回复  |  直到 13 年前
        1
  •  1
  •   Peter Tillemans    14 年前

    一种可能的方法是将模拟对象包装在始终返回的dynamicProxy中。 this 对于作为Fluent API的一部分且没有记录期望的方法。对于记录了期望的方法(或者不是Fluent接口的一部分),它委托给常规模拟对象。

    当然,检测哪些方法定义了期望值,将高度依赖于mocklibrary。不流畅的方法可以很容易地测试使用自省。

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

        2
  •  1
  •   Andy McCluggage hunter    13 年前

    我需要像这样的东西 IQuery 接口。我用了Castle.Dymanicproxy和Rhino.Mocks,下面实现了一个假的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);
            }
        }
    }