17

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

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

当人们想要(存根?)伪造一个持久层以实现快速开发时,他们会做什么实践?我使用依赖注入来处理它,并为我的持久层提供了一些硬编码的结果(这真的很手动而且很无聊)。

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

更新

只是一点点更新:到目前为止,我从拥有一个假存储库和一个 SQL 存储库中获得了相当多的里程——每个类都实现了一个接口。然后,使用 DI(我正在使用 StructureMap),我可以在我的假存储库或 SQL 存储库之间切换。到目前为止,它运行良好:)

(想想我在将近 11 个月前问过这个问题也很可怕,从我现在编辑这个问题开始!)

4

6 回答 6

3

假设您使用 Rob Conery 的 MVC Store Front 中的存储库模式:

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

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

例如

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;
     }
}

这是否更有意义?

于 2008-11-13T13:47:05.600 回答
0

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

于 2008-11-13T14:46:33.950 回答
0

Boring or not, I think you're on the right track. I assume you're creating a fakeRepository that is a concrete implementation of your IRepository which in turn is injected into your service layer. This is nice because at some point in the future when you're happy with the shape of your entities and the behavior of your services, controllers, and views, you can then test drive your real Repositories that will use the database to persist those entities. Of course the nature of those tests will be integration tests, but just as important if not more so.

One thing that may be less boring for you when the time comes to create your real repositories is if you use nHibernate for your persistence you will be able let nhibernate generate your database after you create the nhibernate maps for your entities, assuming you don't have to use a legacy schema.

For instance, I have the following method that is called by my SetUpFixture to generate my db schema:

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

and my SetUpFixture is as follows:

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

where DataLoader is responsible for creating all of my seed data and test data using the real respoitory.

This probably doesn't answer your questions but I hope it serves to reassure you in your approach.

Greg

于 2008-11-13T14:14:16.607 回答
0

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

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

于 2008-11-14T07:46:36.013 回答
0

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

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

然后我们只是添加很多客户或其他任何东西,DDD 风格:

customerRepository.Save(customer);

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

于 2008-11-23T16:32:01.940 回答
0

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

首先,使用RavenDb (Embedded)。它是RavenDb 文档数据库的一部分。它是一个完全在内存中的数据库,并且可以与单元测试完美配合 :) 我已经使用 MSTest、NUnit 和 xUnit 完成了它。

其次,如果您不想使用 RavenDb,可以将 NHibernate 与 SqlLite 一起使用。Ayende 有一篇关于使用这个的帖子

于 2012-01-18T11:43:24.743 回答