我是新的嘲弄。我想嘲笑我的基础信息库是依赖于实体框架6的DbContext,但我失败了。我在谷歌搜索了很多,但没有得到任何充分的结果。最后,我得到了一个例子,在测试与异步查询并努力遵循,但它工作了我的。
I am new in mocking. I want to mock up my base repository which is depend on Entity Framework 6 DbContext But I fail. I searched in Google a lot but did not get any sufficient result. At last I got an example at testing with async queries and try to follow but it is worked for me.
下面是我的code:
的DbContext:
DbContext :
public class TimeSketchContext : DbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
基础信息库:
Base Repository :
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()失败
Assert.NotEqual() Failure
我觉得问题是
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>(); <<<<<<<<<<<
}
但不`吨明白为什么,以及如何解决这个问题。
But don`t understand why and how to solve this.
我使用的:
的Visual Studio 2013旗舰版 起订量 的xUnitThank`s提前。
Thank`s in advance.
您的权利与你的 InnerDbContext.Set&LT的问题; T&GT;();
语句。
You are right the problem in with your InnerDbContext.Set<T>();
statement.
在EF(6.0.2)中的 DbContext.Set&LT; T&GT;
方法是不可以 虚拟
所以不能用嘲笑起订量。
In the current version of the EF (6.0.2) the DbContext.Set<T>
method is not virtual
so it cannot be mocked with Moq.
所以,你不能轻易地让你的测试通过execpt改变你的 BaseRepository
的设计不依赖于整个的DbContext
但有一个 DbSet&LT; T&GT;
:
So you cannot easily making your test pass execpt changing your design of the BaseRepository
to not depend on the whole DbContext
but on one DbSet<T>
:
所以是这样的:
public BaseRepository(DbSet<T> dbSet)
{
InnerDbSet = dbSet;
}
然后你就可以在你的嘲笑Dbset直接通过。
Then you can pass directly in your mocked Dbset.
或者你可以创建一个包装接口的DbContext
:
Or you can create a wrapper interface for DbContext
:
public interface IDbContext
{
DbSet<T> Set<T>() where T : class;
}
public class TimeSketchContext : DbContext, IDbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
然后使用 IDbContext
在 BaseRepository
:
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);
}
}
最后,你只需要改变两行的测试,使其通过:
And finally you just need to change two lines in your test to make it pass:
var mockContext = new Mock<IDbContext>();
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object);