如何模拟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);