【发布时间】:2014-02-28 13:47:45
【问题描述】:
我有一个类似以下的类,我想对其进行单元测试:
public class AddUserCommand
{
IDbContext dbContext;
public AddUserCommand(IDbContext context)
{
dbContext = context;
}
public void Execute()
{
dbContext.Users.Add(new User());
dbContext.SaveChanges();
}
}
最终我需要在使用真正的sql数据库连接时测试Execute方法是否将新用户持久化到数据库中。但是对于我的单元测试,我显然想使用某种模拟对象。 在我的测试中,我可以制作一个模仿行为的模拟 IDbContext,并且一切正常。在运行 Execute 方法后,我可以测试模拟上下文是否包含新用户。
我的问题是,当使用模拟上下文时,如果我不调用 SaveChanges 方法,测试将通过。这是因为模拟上下文不需要进行 sql 查询来实际持久化数据。它在没有调用 SaveChanges 的情况下“持久”,因为 Users 集合代表持久存储。
为了检查 SaveChanges 是否被调用,许多在线资源 (例如: http://msdn.microsoft.com/en-us/library/ff714955.aspx 和 http://msdn.microsoft.com/en-gb/data/dn314431.aspx) 说要在模拟上下文中添加这样的内容:
public class MockDbContext : IDbContext
{
boolean saved;
public void SaveChanges {
saved = true;
}
}
然后在调用Execute方法后测试保存的变量是否为真。但是,我发现这种方法缺乏的是,如果 Execute 方法这样做,这样的测试将通过:
public void Execute()
{
dbContext.SaveChanges();
dbContext.Users.Add(new User());
}
这当然不会保存任何更改,因为它完成得太早了。 我相信像 RhinoMocks 这样的 mocking 框架允许你测试方法调用到 mock 上下文的顺序,但我也读到这不是最佳实践(你应该测试结果,而不是实现的细节)。
问题在于模拟上下文并不能完全复制真实 DbContext 的作用。
所以我的问题是:是否有一种标准方法来模拟实体框架 DbContext,使得对象的任何添加或删除仅在调用 SaveChanges 时提交给模拟?或者这不是通常测试的东西?
【问题讨论】:
标签: c# entity-framework unit-testing mocking