如何对数据库访问逻辑进行单元测试?
How to unit test database access logic?
我有一个基于 EF Code First
使用 MSSQL server
的项目。在我的一个存储库中,我正在使用 PredicateBuilder
动态构建查询。每次代码发生变化时手动测试所有可能的结果,非常耗时。
出于这个原因,我想通过单元测试使其自动化。我正在考虑使用 sql compact
进行单元测试,使用 MSSQL server
进行生产。但是如何为 sql compact
服务器启用迁移?
这是 dbContext class:
public partial class ApplicationDbContext :
IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationDbContext() : base("name=DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
base.OnModelCreating(modelBuilder);
}
}
存储库:
public class FilterRepository : IFilterRepository
{
private ApplicationDbContext _dbContext;
public FilterRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
DbInterception.Add(new CommandInterceptor());
}
public IEnumerable<Person> GetPersons(Filter filter)
{
try
{
var persons = PredicateBuilder.False<Person>();
_dbContext.Configuration.AutoDetectChangesEnabled = false;
var result = _dbContext.Persons
.AsNoTracking()
.Where(persons)
.OrderBy(x => x.Name)
.Skip(filter.Skip)
.Take(10)
.ToList();
_dbContext.Configuration.AutoDetectChangesEnabled = true;
return result;
}
catch (Exception ex)
{
}
}
}
好的,所以切换到不同的数据库进行测试是 (imo) 一个坏主意。
这里有一些术语供您参考,可能有助于您更好地组织测试。
单元测试用于测试您的业务功能,通常您会为此模拟您的数据,因为您想要测试您的域 classes 中的输入(数据)发生了什么,而不是在哪里他们来自.
集成测试用于测试您的业务领域层如何与您的服务层交互(您的数据层是服务层),我也将测试查询的正确性视为集成测试,目的是 运行他们。
因此,在您的情况下,不要混淆事物并可能添加因 2 个数据库工作方式不同而导致的意外行为。如果您正在测试您的业务功能,请为此模拟您的数据。它一开始速度更快,您将测试您想要测试的内容。
通过集成测试,您正在测试与服务层的交互是否正确,在这种情况下,您的测试并不是真正测试业务逻辑是否有效,这应该包含在单元测试中;测试您的查询 return 其谓词的正确数据,以及要保留的任何数据是否正确保留。此外,任何正在进行的交易都按照您的预期进行。场景的端到端测试也是有效的集成测试。
您绝对需要在与生产相同的数据库平台上执行此操作,不要期望 SQL Compact 和 SQL Server 的行为方式相同。
编辑...评论。
因此,您模拟存储库调用的正常方法是使用依赖注入,您不必这样做,但它更容易且几乎是最佳实践。
想法是,在您的域 class 中使用您的数据,您将首先获取您的存储库,或者从注入到构造函数或从中提取的 DI 容器中查询 class DI 容器
// in your domain class you would have something like...
var repo = container.Get<IRepository>();
var myList = repo.GetMyObjects(predcate);
因此,使用 Moq,您现在可以简单地模拟该调用
//Where you do your container registration..
var repo = Mock<IRepository>
repo.Setup( o => o.GetMyObject(predecate)).Returns( (predecate) => <your dummy list>));
container.Register(repo.Object);
// Then later on your business domain object gets the dummy repo instead.
注意这是伪代码,根据使用的 DI 和 Mocking 框架会略有不同。
我有一个基于 EF Code First
使用 MSSQL server
的项目。在我的一个存储库中,我正在使用 PredicateBuilder
动态构建查询。每次代码发生变化时手动测试所有可能的结果,非常耗时。
出于这个原因,我想通过单元测试使其自动化。我正在考虑使用 sql compact
进行单元测试,使用 MSSQL server
进行生产。但是如何为 sql compact
服务器启用迁移?
这是 dbContext class:
public partial class ApplicationDbContext :
IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationDbContext() : base("name=DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
base.OnModelCreating(modelBuilder);
}
}
存储库:
public class FilterRepository : IFilterRepository
{
private ApplicationDbContext _dbContext;
public FilterRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
DbInterception.Add(new CommandInterceptor());
}
public IEnumerable<Person> GetPersons(Filter filter)
{
try
{
var persons = PredicateBuilder.False<Person>();
_dbContext.Configuration.AutoDetectChangesEnabled = false;
var result = _dbContext.Persons
.AsNoTracking()
.Where(persons)
.OrderBy(x => x.Name)
.Skip(filter.Skip)
.Take(10)
.ToList();
_dbContext.Configuration.AutoDetectChangesEnabled = true;
return result;
}
catch (Exception ex)
{
}
}
}
好的,所以切换到不同的数据库进行测试是 (imo) 一个坏主意。
这里有一些术语供您参考,可能有助于您更好地组织测试。
单元测试用于测试您的业务功能,通常您会为此模拟您的数据,因为您想要测试您的域 classes 中的输入(数据)发生了什么,而不是在哪里他们来自.
集成测试用于测试您的业务领域层如何与您的服务层交互(您的数据层是服务层),我也将测试查询的正确性视为集成测试,目的是 运行他们。
因此,在您的情况下,不要混淆事物并可能添加因 2 个数据库工作方式不同而导致的意外行为。如果您正在测试您的业务功能,请为此模拟您的数据。它一开始速度更快,您将测试您想要测试的内容。
通过集成测试,您正在测试与服务层的交互是否正确,在这种情况下,您的测试并不是真正测试业务逻辑是否有效,这应该包含在单元测试中;测试您的查询 return 其谓词的正确数据,以及要保留的任何数据是否正确保留。此外,任何正在进行的交易都按照您的预期进行。场景的端到端测试也是有效的集成测试。
您绝对需要在与生产相同的数据库平台上执行此操作,不要期望 SQL Compact 和 SQL Server 的行为方式相同。
编辑...评论。
因此,您模拟存储库调用的正常方法是使用依赖注入,您不必这样做,但它更容易且几乎是最佳实践。
想法是,在您的域 class 中使用您的数据,您将首先获取您的存储库,或者从注入到构造函数或从中提取的 DI 容器中查询 class DI 容器
// in your domain class you would have something like...
var repo = container.Get<IRepository>();
var myList = repo.GetMyObjects(predcate);
因此,使用 Moq,您现在可以简单地模拟该调用
//Where you do your container registration..
var repo = Mock<IRepository>
repo.Setup( o => o.GetMyObject(predecate)).Returns( (predecate) => <your dummy list>));
container.Register(repo.Object);
// Then later on your business domain object gets the dummy repo instead.
注意这是伪代码,根据使用的 DI 和 Mocking 框架会略有不同。