如何模拟实体框架 6 异步方法



我是嘲笑新手。我想模拟依赖于实体框架 6 DbContext 的基本存储库,但我失败了。我在谷歌上搜索了很多,但没有得到足够的结果。最后,我得到了一个使用异步查询进行测试的示例并尝试遵循,但它对我有用。

这是我的代码:

数据库上下文 :

public class TimeSketchContext : DbContext
{
    public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}

基本存储库 :

public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
    protected readonly DbContext InnerDbContext;
    protected DbSet<T> InnerDbSet;
    public BaseRepository(DbContext innerDbContext)
    {
        InnerDbContext = innerDbContext;
        InnerDbSet = InnerDbContext.Set<T>();
    }
    public virtual Task<T> FindAsync(long id)
    {
        return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id);
    }

}

测试:

    [Fact]
    public async Task DbTest()
    {
        var dummyData = GetEmployeeSkills();
        var mockSet = new Mock<DbSet<EmployeeSkill>>();
        mockSet.As<IDbAsyncEnumerable<EmployeeSkill>>()
            .Setup(x => x.GetAsyncEnumerator())
            .Returns(new TestDbAsyncEnumerator<EmployeeSkill>(dummyData.GetEnumerator()));
        mockSet.As<IQueryable<EmployeeSkill>>()
            .Setup(x => x.Provider)
            .Returns(new TestDbAsyncQueryProvider<EmployeeSkill>(dummyData.Provider));
        mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.Expression).Returns(dummyData.Expression);
        mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.ElementType).Returns(dummyData.ElementType);
        mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.GetEnumerator()).Returns(dummyData.GetEnumerator());
        var mockContext = new Mock<TimeSketchContext>();
        mockContext.Setup(c => c.EmployeeSkill).Returns(mockSet.Object);
        var baseRepository = new BaseRepository<EmployeeSkill>(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<EmployeeSkill> GetEmployeeSkills()
    {
        return new List<EmployeeSkill>
        {
            GetEmployeeSkill(),
            GetEmployeeSkill(),
            GetEmployeeSkill(),
        }.AsQueryable();
    }

结果是 :

Assert.NotEqual() 失败

我认为问题是

 public BaseRepository(DbContext innerDbContext)
 {
     InnerDbContext = innerDbContext;
     InnerDbSet = InnerDbContext.Set<T>();  <<<<<<<<<<<
 }

但是不明白为什么以及如何解决这个问题。

我正在使用:

  • Visual Studio 2013 Ultimate
  • 最小起订量
  • x单位
提前

谢谢。

你是对的,问题出在你的InnerDbContext.Set<T>();陈述中。

在当前版本的 EF (6.0.2) 中,DbContext.Set<T> 方法不是virtual,因此无法使用 Moq 进行模拟。

因此,除非更改BaseRepository的设计,不依赖于整个DbContext而是依赖于一个DbSet<T>,否则您无法轻松通过测试:

所以像这样:

public BaseRepository(DbSet<T> dbSet)
{
    InnerDbSet = dbSet;
}

然后,您可以直接在模拟的 DbSet 中传递。

或者你可以为DbContext创建一个包装器接口:

public interface IDbContext
{
    DbSet<T> Set<T>() where T : class;
}
public class TimeSketchContext : DbContext, IDbContext
{
    public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}

然后在BaseRepository中使用IDbContext

public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
    protected readonly IDbContext InnerDbContext;
    protected DbSet<T> InnerDbSet;
    public BaseRepository(IDbContext innerDbContext)
    {
        InnerDbContext = innerDbContext;
        InnerDbSet = InnerDbContext.Set<T>();
    }
    public virtual Task<T> FindAsync(long id)
    {
        return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id);
    }
}

最后,您只需更改测试中的两行即可使其通过:

var mockContext = new Mock<IDbContext>();
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object);

最新更新