在执行ASP.NET站点(例如ASP.NET MVC站点)时创建测试持久层的最佳实践是什么?
我见过很多在单元测试项目中使用Moq(或其他模拟框架)的例子,但是我想,像moq我的持久层,以便我的网站显示数据和内容,但它不是来自一个数据库。我想做到最后。我见过的所有嘲弄的东西只存在于单元测试中。
当人们想要(存根?)伪造一个持久层以便快速快速开发时,人们会采取什么样的做法?我使用依赖注入来处理它,并为我的持久层提供了一些硬编码结果(这实际上是手动和无聊的。)
其他人在做什么?示例和链接非常棒:)
稍微更新一下:到目前为止,我有一个假的存储库和一个SQL存储库 - 每个类都实现了一个接口。然后,使用DI(我正在使用StructureMap),我可以在我的虚拟存储库或SQL存储库之间切换。到目前为止,它运作良好:))
(同样可怕的是,我认为我差不多11个月前就问这个问题了,从我编辑这个问题开始,现在就开始!)
答案 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框架,但我确实需要测试服务而不需要访问数据库。你的问题引发了我如何做的简短(好的,可能不那么短)的总结。没有声称它是最好的或任何东西,但它适用于我们。我们通过存储库访问数据,并在需要时插入内存存储库,如帖子中所述。
答案 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