如何模拟Entity Framework 6异步方法?
我是嘲笑的新手。 我想模拟我的基础存储库,它依赖于Entity Framework 6 DbContext但是我失败了。 我在Google搜索了很多但没有得到足够的结果。 最后,我得到了一个测试异步查询的例子,并尝试遵循,但它对我有用。
这是我的代码:
DbContext:
public class TimeSketchContext : DbContext { public virtual DbSet EmployeeSkill { get; set; } }
基础知识库:
public class BaseRepository : IRepositoryBase where T : class, IEntity, new() { protected readonly DbContext InnerDbContext; protected DbSet InnerDbSet; public BaseRepository(DbContext innerDbContext) { InnerDbContext = innerDbContext; InnerDbSet = InnerDbContext.Set(); } public virtual Task FindAsync(long id) { return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id); }
}
测试:
[Fact] public async Task DbTest() { var dummyData = GetEmployeeSkills(); var mockSet = new Mock<DbSet>(); mockSet.As<IDbAsyncEnumerable>() .Setup(x => x.GetAsyncEnumerator()) .Returns(new TestDbAsyncEnumerator(dummyData.GetEnumerator())); mockSet.As<IQueryable>() .Setup(x => x.Provider) .Returns(new TestDbAsyncQueryProvider(dummyData.Provider)); mockSet.As<IQueryable>().Setup(m => m.Expression).Returns(dummyData.Expression); mockSet.As<IQueryable>().Setup(m => m.ElementType).Returns(dummyData.ElementType); mockSet.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(dummyData.GetEnumerator()); var mockContext = new Mock(); mockContext.Setup(c => c.EmployeeSkill).Returns(mockSet.Object); var baseRepository = new BaseRepository(mockContext.Object); var data = await baseRepository.FindAsync(1); Assert.NotEqual(null, data); } private EmployeeSkill GetEmployeeSkill() { return new EmployeeSkill { SkillDescription = "SkillDescription", SkillName = "SkillName", Id = 1 }; } private IQueryable GetEmployeeSkills() { return new List { GetEmployeeSkill(), GetEmployeeSkill(), GetEmployeeSkill(), }.AsQueryable(); }
结果是:
Assert.NotEqual()失败
我认为问题是
public BaseRepository(DbContext innerDbContext) { InnerDbContext = innerDbContext; InnerDbSet = InnerDbContext.Set(); <<<<<<<<<<< }
但不明白为什么以及如何解决这个问题。
我在用 :
- Visual Studio 2013 Ultimate
- 起订量
- 的xUnit
提前谢谢。
你是对的问题出在你的InnerDbContext.Set
声明。
在当前版本的EF(6.0.2)中, DbContext.Set
方法 不是 virtual
因此无法使用Moq进行virtual
。
因此,除非将BaseRepository
的设计更改为不依赖于整个DbContext
而是依赖于一个DbSet
否则您无法轻松进行测试通过:
所以类似于:
public BaseRepository(DbSet dbSet) { InnerDbSet = dbSet; }
然后你可以直接在你的模拟DbSet中传递。
或者您可以为DbContext
创建一个包装器接口:
public interface IDbContext { DbSet Set () where T : class; } public class TimeSketchContext : DbContext, IDbContext { public virtual DbSet EmployeeSkill { get; set; } }
然后在BaseRepository
使用BaseRepository
:
public class BaseRepository : IRepositoryBase where T : class, IEntity, new() { protected readonly IDbContext InnerDbContext; protected DbSet InnerDbSet; public BaseRepository(IDbContext innerDbContext) { InnerDbContext = innerDbContext; InnerDbSet = InnerDbContext.Set (); } public virtual Task FindAsync(long id) { return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id); } }
最后,您只需要在测试中更改两行以使其通过:
var mockContext = new Mock(); mockContext.Setup(c => c.Set()).Returns(mockSet.Object);