如何在进行TDD时最好地创建测试数据库?

时间:2008-11-13 13:13:26

标签: asp.net tdd dependency-injection mocking persistence

在执行ASP.NET站点(例如ASP.NET MVC站点)时创建测试持久层的最佳实践是什么?

我见过很多在单元测试项目中使用Moq(或其他模拟框架)的例子,但是我想,像moq我的持久层,以便我的网站显示数据和内容,但它不是来自一个数据库。我想做到最后。我见过的所有嘲弄的东西只存在于单元测试中。

当人们想要(存根?)伪造一个持久层以便快速快速开发时,人们会采取什么样的做法?我使用依赖注入来处理它,并为我的持久层提供了一些硬编码结果(这实际上是手动和无聊的。)

其他人在做什么?示例和链接非常棒:)

更新

稍微更新一下:到目前为止,我有一个假的存储库和一个SQL存储库 - 每个类都实现了一个接口。然后,使用DI(我正在使用StructureMap),我可以在我的虚拟存储库或SQL存储库之间切换。到目前为止,它运作良好:))

(同样可怕的是,我认为我差不多11个月前就问这个问题了,从我编辑这个问题开始,现在就开始!)

6 个答案:

答案 0 :(得分:3)

假设您正在使用Rob Conery的MVC Store Front中的Repository模式:

http://blog.wekeroad.com/mvc-storefront/mvc-storefront-part-1/

我遵循了Rob Conery的教程,但遇到了和你一样的想法。最好的办法是将你创建的模拟存储库移动到一个名为Mocks的单独项目中,然后在实例化服务时,可以很容易地将它们与真实的模块进行交换。如果您喜欢冒险,您可以创建一个工厂,从配置文件中获取值来实例化模拟或真实存储库,

e.g。

public static ICatalogRepository GetCatalogRepository(bool useMock)
{
     if(useMock)
          return new FakeCatalogRepository();
     else
          return new SqlCatalogRepository();
}

或使用依赖注入框架:)

container.Resolve<ICatalogRepository>();
祝你好运!

编辑:为了回应您的评论,听起来您希望使用列表和LINQ来模拟数据库的操作,例如GetProducts,StoreProduct。我之前做过这个。这是一个例子:

public class Product
{
     public int Identity { get; set; }
     public string Name { get; set; }
     public string Description { get; set; }
     //etc
}

public class FakeCatalogRepository()
{
     private List<Product> _fakes;

     public FakeCatalogCatalogRepository()
     {
          _fakes = new List<Product>();

          //Set up some initial fake data
          for(int i=0; i < 5; i++)
          {
              Product p = new Product
              {
                 Identity = i,
                 Name = "product"+i,
                 Description = "description of product"+i
              };

              _fakes.Add(p);
          }
     }

     public void StoreProduct(Product p)
     {
         //Emulate insert/update functionality

         _fakes.Add(p);
     }

     public Product GetProductByIdentity(int id)
     {
          //emulate "SELECT * FROM products WHERE id = 1234
          var aProduct = (from p in _fakes.AsQueryable()
                         where p.Identity = id
                         select p).SingleOrDefault();

          return aProduct;
     }
}

这是否更有意义?

答案 1 :(得分:0)

无聊与否,我认为你走在正确的轨道上。我假设您正在创建一个fakeRepository,它是您的IRepository的具体实现,而后者又被注入您的服务层。这很好,因为在将来某个时候,当您对实体的形状以及服务,控制器和视图的行为感到满意时,您可以测试驱动您将使用数据库来保存这些实体的真实存储库。 。当然,这些测试的性质将是集成测试,但同样重要,如果不是更重要的话。

在创建真正的存储库时,有些事情可能不那么无聊,如果你使用nHibernate进行持久化,那么在为你的实体创建nhibernate映射后,你可以让nhibernate生成你的数据库,假设你不必使用旧架构。

例如,我有以下方法由我的SetUpFixture调用来生成我的数据库模式:

public class SchemaBuilder
{
   public static void ExportSchema()
    {
        Configuration configuration = new Configuration();
        configuration.Configure();
        new SchemaExport(configuration).Create(true, true);
    }
}

我的SetUpFixture如下:

[SetUpFixture]
public class SetUpFixture
{
    [SetUp]
    public void SetUp()
    {
        SchemaBuilder.ExportSchema();
        DataLoader.LoadData();
    }
}

其中DataLoader负责使用真实的respoitory创建我的所有种子数据和测试数据。

这可能无法解答您的问题,但我希望您的方法可以让您放心。

格雷格

答案 2 :(得分:0)

我已经在单元测试类中的设置方法中创建表和数据,运行测试,然后在拆解期间进行清理。是的,这种方法有效,但是如果你真的最终使用你的单元测试进行调试,你总是会运行设置,调试一些东西,然后停在中间而不进行拆卸。它非常脆弱,您可能会在测试数据库和/或不可用的单元测试中使用错误数据(从长远来看)。我个人认为最好使用模拟框架来模拟数据库层。我明白有时候最好在数据库中做逻辑。对于这些情况,您可以使用DBFit之类的工具为数据库层编写测试。

答案 3 :(得分:0)

虽然我没有使用Asp.Net或MVC框架,但我确实需要测试服务而不需要访问数据库。你的问题引发了我如何做的简短(好的,可能不那么短)的总结。没有声称它是最好的或任何东西,但它适用于我们。我们通过存储库访问数据,并在需要时插入内存存储库,如帖子中所述。

http://blogs.microsoft.co.il/blogs/kim/archive/2008/11/14/testable-data-access-with-the-repository-pattern.aspx

答案 4 :(得分:0)

我在SQLite和ActiveRecord中使用完整的内存数据库。基本上,我们在运行每个集成测试之前删除并重新创建数据库,以便数据始终处于已知状态。数据库的内容通过代码插入。所以一个例子是这样的:

ActiveRecord.Initalize(lots of parameters)
ActiveRecord.DropSchema();
ActiveRecord.CreateSchema();

然后我们只是添加了许多客户或其他,DDD风格:

customerRepository.Save(customer);

解决此问题的另一种方法是使用NDbUnit来维护数据库的状态。

答案 5 :(得分:0)

我知道这个问题有点老了,但我终于想出了答案:)

首先,使用RavenDb (Embedded)。它是RavenDb Document Database的一部分。它是一个完全内存的数据库,与单元测试完美配合:)我用MSTest,NUnit和xUnit完成了它。

其次,如果您不想使用RavenDb,可以将NHibernate与SqlLite一起使用。 Ayende has a post about using this