我的应用程序中使用DbContext
(EF6)设置了典型的存储库模式:
public class MyDbContext : EFContext<MyDbContext> {
public MyDbContext () { }
public virtual DbSet<CartItem> Cart { get; set; }
和存储库:
public class GenericEFRepository<TEntity, TContext>
where TEntity : class, new()
where TContext : EFContext<TContext> {
private readonly TContext _context;
public GenericEFRepository(TContext context) {
_context = context;
}
//...
public virtual TEntity Insert(TEntity item) {
return _context.Set<TEntity>().Add(item);
}
我正在使用Moq 4.2(this tutorial之后)通过创建模拟上下文来测试它:
// Arrange
var mockSet = new Mock<DbSet<CartItem>>();
var mockContext = new Mock<MyDbContext>();
mockContext.Setup(c => c.Cart).Returns(mockSet.Object);
// Act
var service = new GenericEFRepository<CartItem, MyDbContext>(mockContext.Object);
service.Insert(new CartItem() {
Id = 1,
Date = DateTime.Now,
UserId = 1,
Detail = string.Empty
});
// Assert
mockSet.Verify(s => s.Add(It.IsAny<CartItem>()), Times.Once());
问题在于,当我到达这一行时:
return _context.Set<TEntity>().Add(item);
_context.Set<TEntity>()
返回null。经过一些谷歌搜索似乎在EF5中,有必要返回IDbSet<T>
为Moq模拟集合,但不是EF6。是不是这样,或者我错过了什么?
答案 0 :(得分:15)
为Set<T>()
方法添加设置:
mockContext.Setup(c => c.Set<CartItem>()).Returns(mockSet.Object);
即使真实的EFContext
属性Cart
和Set<CartItem>()
引用同一个对象,上下文的 mock 也不知道,所以你需要明确告诉它要返回什么。
由于它是一个松散的模拟,因此对尚未设置的方法的调用将返回默认值,在本例中为null
。严格的模拟很有助于发现此错误,但也have maintenance costs that other folks don't want to deal with。
答案 1 :(得分:0)
对于2020年底,使用EntitiFramework Core来说,此解决方案对我仍然正确。 要理解如何模拟对象/数据集不是那么容易,我现在开始使用带有模拟数据的内存数据库来实现一些集成测试。 我看到自己的方法可以正常工作,例如:
await _dbcontext.MyEntirySet.ToListAsync();
但在通用存储库中使用等价时失败
_dbcontext.Set<TEntity> : this return a null dataset.
即使使用EntityFramework Core,我也可以确认模拟Set可以解决问题。
_dbContextMock.Setup(c => c.Set<MyEntityType>()).Returns(mock.Object);