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

C# 如何模拟实体框架6异步方法?,c#,.net,entity-framework,unit-testing,moq,C#,.net,Entity Framework,Unit Testing,Moq,我不太会嘲弄人。我想模拟我的基础存储库,它依赖于EntityFramework6DbContext,但我失败了。我在谷歌上搜索了很多,但没有得到足够的结果。最后,我得到了一个例子,并试图遵循,但它是为我工作 这是我的密码: DbContext: public class TimeSketchContext : DbContext { public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; } } publi

我不太会嘲弄人。我想模拟我的基础存储库,它依赖于EntityFramework6DbContext,但我失败了。我在谷歌上搜索了很多,但没有得到足够的结果。最后,我得到了一个例子,并试图遵循,但它是为我工作

这是我的密码:

DbContext:

public class TimeSketchContext : DbContext
{
    public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
public类TimeSketchContext:DbContext
{
公共虚拟数据库集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);
    }
public-class-BaseRepository:IRepositoryBase其中T:class,IEntity,new()
{
受保护的只读DbContext InnerDbContext;
受保护的DbSet-innerdset;
公共基本存储库(DbContext innerDbContext)
{
InnerDbContext=InnerDbContext;
InnerDbSet=InnerDbContext.Set();
}
公共虚拟任务FindAsync(长id)
{
返回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();
    }
[事实]
公共异步任务DbTest()
{
var dummyData=GetEmployeeSkills();
var mockSet=new Mock();
mockSet.As()
.Setup(x=>x.GetAsyncEnumerator())
.Returns(新的TestDbAsyncEnumerator(dummyData.GetEnumerator());
mockSet.As()
.Setup(x=>x.Provider)
.Returns(新的TestDbAsyncQueryProvider(dummyData.Provider));
mockSet.As().Setup(m=>m.Expression).Returns(dummyData.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(dummyData.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator()).Returns(dummyData.GetEnumerator());
var mockContext=new Mock();
Setup(c=>c.EmployeeSkill).Returns(mockSet.Object);
var baseRepository=新的baseRepository(mockContext.Object);
var data=await baseRepository.FindAsync(1);
Assert.NotEqual(null,数据);
}
私人雇员技能GetEmployeeSkill()
{
返回新员工技能
{
SkillDescription=“SkillDescription”,
SkillName=“SkillName”,
Id=1
};
}
私有IQueryable GetEmployeeSkills()
{
返回新列表
{
GetEmployeeSkill(),
GetEmployeeSkill(),
GetEmployeeSkill(),
}.AsQueryable();
}
结果是:

Assert.NotEqual()失败

我想问题是

 public BaseRepository(DbContext innerDbContext)
 {
     InnerDbContext = innerDbContext;
     InnerDbSet = InnerDbContext.Set<T>();  <<<<<<<<<<<
 }
公共基础存储库(DbContext innerDbContext)
{
InnerDbContext=InnerDbContext;

InnerDbSet=InnerDbContext.Set();您是对的,问题出在您的
InnerDbContext.Set();
语句中

在EF(6.0.2)的当前版本中,它不是虚拟的,因此不能用Moq模拟

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

比如:

public BaseRepository(DbSet<T> dbSet)
{
    InnerDbSet = dbSet;
}
然后在
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);
    }
}
public-class-BaseRepository:IRepositoryBase其中T:class,IEntity,new()
{
受保护的只读IDbContext InnerDbContext;
受保护的DbSet-innerdset;
公共基本存储库(IDbContext innerDbContext)
{
InnerDbContext=InnerDbContext;
InnerDbSet=InnerDbContext.Set();
}
公共虚拟任务FindAsync(长id)
{
返回InnerDbSet.FirstOrDefaultAsync(x=>x.Id==Id);
}
}
最后,您只需更改测试中的两行代码即可通过测试:

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

面向未来读者:如果您在任何地方都注入了DbContext,并且无法将其抽象到存储库/接口,那么您绝对需要“模拟”由于它是非虚拟的,所以您可以使用Microsoft Fakes程序集来实现这一点,因为它提供了一些奇特的IL功能,让您甚至可以在对象上提供非虚拟方法的替代实现。我强烈建议您查看“努力”(),一个EF单元测试工具,它在内存中创建了一个足够真实的EF数据库来进行测试。我们在模拟EF时也经历了很多麻烦,但最终,测试EF mock是一场彻头彻尾的失败。(例如,模拟测试使用L2O而不是L2E。)它看起来是一个不错的工具,稍后会检查。不管怎样感谢@rjbforef6,我发现本文中的代码非常有用:我还遇到了模仿AsNoTracking()的问题,这个聪明的答案解决了这个问题:这个努力url在Google上给出了一个关于前方危险程序的警告,并建议不要继续。向下投票是因为方法是
FindAsync
,但实现是
FirstAndDefaultAsync
var mockContext = new Mock<IDbContext>();
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object);