Mat*_*yde 7 c# unit-testing entity-framework mocking
我有一个类似下面的类,我想进行单元测试:
public class AddUserCommand
{
IDbContext dbContext;
public AddUserCommand(IDbContext context)
{
dbContext = context;
}
public void Execute()
{
dbContext.Users.Add(new User());
dbContext.SaveChanges();
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我需要测试在使用真正的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)说要在mock环境中添加这样的东西:
public class MockDbContext : IDbContext
{
boolean saved;
public void SaveChanges {
saved = true;
}
}
Run Code Online (Sandbox Code Playgroud)
然后在调用Execute方法后测试保存的变量是否为true.但是,我发现这种方法缺少的是,如果Execute方法执行此操作,则会传递此类测试:
public void Execute()
{
dbContext.SaveChanges();
dbContext.Users.Add(new User());
}
Run Code Online (Sandbox Code Playgroud)
这当然不会保存任何更改,因为它太早完成.我相信像RhinoMocks这样的模拟框架允许你测试模拟上下文的方法调用顺序,但我也读过这不是最佳实践(你应该测试结果,而不是实现的minutae).
问题是模拟上下文并没有完全复制真正的DbContext将要做的事情.
所以我的问题是:是否有一种标准方法来模拟实体框架DbContext,以便在调用SaveChanges时,对象的任何添加或删除都只提交给模拟?或者这不是通常测试的东西?
Tre*_*ley 13
您应该能够使用Moq框架执行此操作:
// Counters to verify call order
int callCount = 0;
int addUser = 0;
int saveChanges = 0;
// use Moq to create a mock IDbContext.
var mockContext = new Mock<IDbContext>();
// Register callbacks for the mocked methods to increment our counters.
mockContext.Setup(x => x.Users.Add(It.IsAny<User>())).Callback(() => addUser = callCount++);
mockContext.Setup(x => x.SaveChanges()).Callback(() => saveChanges = callCount++);
// Create the command, providing it the mocked IDbContext and execute it
var command = new AddUserCommand(mockContext.Object);
command.Execute();
// Check that each method was only called once.
mockContext.Verify(x => x.Users.Add(It.IsAny<User>()), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Once());
// check the counters to confirm the call order.
Assert.AreEqual(0, addUser);
Assert.AreEqual(1, saveChanges);
Run Code Online (Sandbox Code Playgroud)
关于这个答案的评论之后,似乎有些人忽略了单元测试的重点以及在代码中使用抽象的目的.
你在这里做的是验证行为,AddUserCommand
这就是全部 - 你正在确认AddUserCommand
该类正在添加用户并保存对上下文的更改.
使用该IDbContext
接口的原因是,您可以单独测试AddUserCommand
该类,而无需在已知状态下提供数据库.你不需要测试真实的实现,DbContext
因为它应该有它自己的单元测试,也可以单独覆盖它.
您可能还需要创建一个集成测试,你会用实际DbContext
并确认记录进入数据库,但就是没有一个单元测试做什么.
归档时间: |
|
查看次数: |
9737 次 |
最近记录: |