代码之家  ›  专栏  ›  技术社区  ›  rafek

喜欢TDD的单人课

  •  2
  • rafek  · 技术社区  · 16 年前

    我有至少2个其他类使用的存储库类。这个存储库类需要初始化——这是成本很高的(查询数据库)。现在,我在需要的地方创建存储库的单独实例。问题是,每次创建存储库时,都必须对其进行初始化。如何将这种存储库设计为对TDD友好的?我想的第一件事是单身,但是 not the solution .

    5 回复  |  直到 15 年前
        1
  •  1
  •   Mladen Mihajlovic    16 年前

    您是否使用任何类型的IOC集装箱? Unity 是我选择的容器,它包含 ContainerControledLifetimeManager 这使你的班成了单身汉,但不是你自己管理的。

        2
  •  3
  •   Jon Limjap    16 年前

    我希望对TDD友好,您的意思是“可测试的”代码。对于单例ObjectX,我认为最常见的方法是 分担责任 “控制创建”到另一个类的(srp),这样ObjectX就可以完成它应该做的所有事情。

    然后,您有另一个类objectxfactory或host,或者您想称之为的任何东西,它负责为所有客户机提供一个实例(并在需要时提供线程同步,等等)。

    • 对象X可以独立进行TDDED。您可以在测试用例和测试功能中创建一个新实例。
    • 另一方面,ObjectXFactory也很容易测试。您只需要查看多个getInstance()调用是否返回同一对象。或者更好地将此职责委托给一个类似Spring的IOC框架,它允许您声明性地标记一个对象定义以获得单例行为(也节省了编写测试的工作量)。

    您只需要教育并遵守团队约定,即不调用ObjectX构造函数-始终使用ObjectXFactory.CreateInstance()。(如果您发现您有意识/纪律问题,则将ObjectX的ctor标记为内部的,并且只能通过 InternalsVisibleToAttribute ) 高温高压

        3
  •  2
  •   Iain Holder    16 年前

    TDD部分的一个答案是学习模仿。

    看看斯蒂芬·沃尔特的这篇优秀文章:

    http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx

        4
  •  1
  •   Preet Sangha    16 年前

    在考虑单例之前,考虑缓存实例以提高性能。但是对于TDD友好的设计,考虑策略注入,以便“慢”位可以被删除用于测试,并替换为存根和模拟。如果可以,尽量不要在测试中进行db调用。

        5
  •  1
  •   Jon Limjap    16 年前

    你不能这样做——至少在真正的TDD意义上是不行的。

    依赖DI/IOC策略(如Unity)意味着您的测试依赖于外部组件,而不是孤立测试。

    然后这些测试变成集成测试,而不是单元测试。

    ==忽略下面的答案==

    我想你想知道如何使存储库可测试。

    为它引入一个接口将允许您模拟或存根它。 从而确保可以独立于存储库的任何具体实现来测试对象。

    我用这个来说明 Rhino Mocks 3.5 for .NET 3.5 :

    让我们从存储库中创建一个接口,让我们调用它 IRepository

    public interface IRepository
    {
    }
    

    现在,由于您需要对两个不同的对象使用IRepository,那么让我们只使用泛型,这样您就可以用它来实例化存储库:

    public interface IRepository<T>
    

    当然,这意味着您将拥有某种查找方法:

    {
        public IEnumerable<T> Find(Criteria criteria)
    }
    

    其中,criteria对象是允许您设置要查找的对象,例如,where子句。

    现在,您有了您的对象:

    public class SomeObject
    {
        IRepository<SomeObject> repository;
    
        public SomeObject(){}
    
        public IRepository<SomeObject> repository { get; set; }
    
        IEnumerable<SomeObject> FindAll()
        {
            //let's assume new Criteria() will return all results
            return respository.Find(new Criteria());
        }
    }
    

    你想测试一下 SomeObject 这样findall()将返回一组预期的结果——这就是Rhino模拟将出现的地方:

    [TestFixture]
    public class SomeObjectTests
    {
        [Test]
        public void TestSomeObjectFindAll()
        {
            IRepository<SomeObject> stubRepository = MockRepsitory.GenerateStub<IRepsitory<SomeObject>>();
    
            stubRepository.Expect(r => r.Find(new Criteria())
                .Return(new List<SomeObject>{ 
                            new SomeObject(), 
                            new SomeObject(), 
                            new SomeObject());
    
            var testObject = new SomeObject { Repository = stubRepository };
            IList<SomeObject> findAllResult = testObject.FindAll();
    
            //returned list contains 3 elements, as expected above
            Assert.AreEqual(3, findAllResult.Count)
        }
    }
    

    请注意,上面的代码并不是TDD在所有方面的最佳实践,但它是一个开始的地方。

    这里的关键概念是 引入接口以允许对象的松散耦合 尤其是当对象倾向于执行诸如访问数据库、文件系统等操作时。

    有一个更全面的例子和更好的例子 Ben Hall's article on Rhino Mocks .