TDD友好的类似Singleton的类

时间:2009-04-23 06:18:53

标签: tdd singleton repository-pattern

我有至少2个其他类使用的存储库类。需要初始化此存储库类 - 成本高(查询数据库)。现在,我在任何需要的地方创建了独立的存储库实例。问题是,每次我创建存储库时都必须进行初始化。如何设计这样的存储库是TDD友好的?在我看来,第一件事是Singleton,但它是not the solution

5 个答案:

答案 0 :(得分:3)

我希望TDD友好,你的意思是'可测试'代码。对于Singleton ObjectX,我认为最常见的方法是将“控制创建”的责任(SRP)拆分为另一个类,以便ObjectX完成它应该做的所有事情。

然后你有另一个类ObjectXFactory或Host或任何你想称之为负责为所有客户端提供单个实例的东西(并在需要时提供线程同步等等)

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

您只需要教育并遵守不要调用ObjectX构造函数的Team约定 - 始终使用ObjectXFactory.CreateInstance()。 (如果您发现自己存在意识/纪律问题,请将ObjectX的ctor标记为内部且仅通过偷偷摸摸的InternalsVisibleToAttribute对测试程序集可见 HTH

答案 1 :(得分:2)

TDD部分的一个答案是学习嘲弄。

查看Stephen Walther撰写的这篇优秀文章:

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

答案 2 :(得分:1)

您使用任何类型的IOC容器吗? Unity是我选择的容器,它包含一个ContainerControledLifetimeManager,它使您的班级成为单身人士,但不是由您自己管理。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

你不能这样做 - 至少不是真正的TDD意义。

依赖于Unity等DI / IoC策略意味着您的测试依赖于外部组件,而不是单独测试。

然后测试成为集成测试,而不是单元测试。

==忽略下面的答案==

我想你想知道如何使Repository可测试。

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

我将使用Rhino Mocks 3.5 for .NET 3.5来说明这一点:

让我们从Repository中创建一个接口,让我们称之为IRepository

public interface IRepository
{
}

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

public interface IRepository<T>

当然这意味着你会有某种find方法:

{
    public IEnumerable<T> Find(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 Mocks的用武之地:

[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上有一个更全面的示例和更好的示例。