一直在谷歌上搜索如何模拟EF6中的DBSet的include方法的解决方案。
这个问题在这里有很好地记录:
http://entityframework.codeplex.com/discussions/461731
不幸的是,似乎那里并没有一个有效的解决办法。
有人找到了解决方法吗?
我知道我们实际上不应该模拟EF6上下文,但项目负责人坚持这么做。
提前感谢。
一直在谷歌上搜索如何模拟EF6中的DBSet的include方法的解决方案。
这个问题在这里有很好地记录:
http://entityframework.codeplex.com/discussions/461731
不幸的是,似乎那里并没有一个有效的解决办法。
有人找到了解决方法吗?
我知道我们实际上不应该模拟EF6上下文,但项目负责人坚持这么做。
提前感谢。
我遇到了与@GetFuzzy相同的问题 - 似乎无论我做什么,每当在Moq DbSet上进行Include()调用时都无法避免NullReferenceException。不幸的是,另一个答案中的Github示例并没有起作用:Set.Include()始终返回null。
经过一番摆弄,我想出了一个解决方法。
[Test]
public void CanUseIncludeWithMocks()
{
var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
var parents = new List<Parent> { parent };
var children = new List<Child> { child };
var parentsDbSet1 = new FakeDbSet<Parent>();
parentsDbSet1.SetData(parents);
var parentsDbSet2 = new FakeDbSet<Parent>();
parentsDbSet2.SetData(parents);
parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet2.Object);
// Can now test a method that does something like: context.Set<Parent>().Include("Children") etc
}
public class FakeDbSet<T> : Mock<DbSet<T>> where T : class
{
public void SetData(IEnumerable<T> data)
{
var mockDataQueryable = data.AsQueryable();
As<IQueryable<T>>().Setup(x => x.Provider).Returns(mockDataQueryable.Provider);
As<IQueryable<T>>().Setup(x => x.Expression).Returns(mockDataQueryable.Expression);
As<IQueryable<T>>().Setup(x => x.ElementType).Returns(mockDataQueryable.ElementType);
As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(mockDataQueryable.GetEnumerator());
}
}
我真的不喜欢必须有两个虚假的DbSets,但出于某种原因,这不起作用:
parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet1.Object);
set.Setup(s => s.Include(It.IsAny<string>())).Returns(set.Object);
对我来说可行。 - Sams.Include(It.IsAny<string>())
确实起作用。太棒了! - krillgarFakeDbSet<T>
构造函数中添加以下内容作为最后一行,这样我就不需要在所有的测试中重复写了:
Setup(m => m.Include(It.IsAny<string>())).Returns(Object);
目前,我使用这种方法成功地运行了许多使用Moq的单元测试。 - KarlZ public static Mock<DbSet<T>> GetMockSet<T>(this ObservableCollection<T> list) where T : class
{
var queryable = list.AsQueryable();
var mockList = new Mock<DbSet<T>>(MockBehavior.Loose);
mockList.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockList.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockList.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockList.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
mockList.Setup(m => m.Include(It.IsAny<string>())).Returns(mockList.Object);
mockList.Setup(m => m.Local).Returns(list);
mockList.Setup(m => m.Add(It.IsAny<T>())).Returns((T a) => { list.Add(a); return a; });
mockList.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Add(item); return a; });
mockList.Setup(m => m.Remove(It.IsAny<T>())).Returns((T a) => { list.Remove(a); return a; });
mockList.Setup(m => m.RemoveRange(It.IsAny<IEnumerable<T>>())).Returns((IEnumerable<T> a) => { foreach (var item in a.ToArray()) list.Remove(item); return a; });
return mockList;
}
使用它只需执行以下操作:
mockContext.Setup(p => p.<DbSetToMock>).Returns(<observableCollection to use as data>.GetMockSet().Object);`
如果上下文实现了接口,这将非常有效,因为您永远不必处理EF。
编辑:
额外位的原因是我们可以在测试中检查结果,如果添加或删除,我们可以检查通过的集合,它将在测试后具有结果。
Include (Expresssion<Func<T, TProperty>>)
重载 - 但你不能因为它是扩展方法 - 实际上你只需要模拟核心 Include(string)
方法就可以让它起作用。 - oatsodavar list = new ObservableCollection<PermissionList>(_permissionLists); _mockContext.Setup(m => m.PermissionLists).Returns(list.GetMockSet().Object);
- RandomHandle*Async
方法(例如 IQueryable<T>.ToListAsync()
)的代码,您需要采用一些额外的样板文件:https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking#testing-with-async-queries:mockList.As>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator(queryable.GetEnumerator()));
mockList.As>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider(queryable.Provider));
- JoeBrockhaus所以,如果稍微费点力就可以实现这个功能!
以下是我设置的模拟上下文和集合,并成功调用了include。我认为秘密在于通过对Provider、Expression和GetEnumerator的调用进行存根处理,并在将DbSet属性设置为存根集合时不将上下文存根化返回它们。
[Test]
public void CanUseIncludeWithMocks()
{
var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
var parents = new List<Parent>
{
parent
}.AsQueryable();
var children = new List<Child>
{
child
}.AsQueryable();
var mockContext = MockRepository.GenerateStub<TestContext>();
var mockParentSet = MockRepository.GenerateStub<IDbSet<Parent>>();
var mockChildSet = MockRepository.GenerateStub<IDbSet<Child>>();
mockParentSet.Stub(m => m.Provider).Return(parents.Provider);
mockParentSet.Stub(m => m.Expression).Return(parents.Expression);
mockParentSet.Stub(m => m.GetEnumerator()).Return(parents.GetEnumerator());
mockChildSet.Stub(m => m.Provider).Return(children.Provider);
mockChildSet.Stub(m => m.Expression).Return(children.Expression);
mockChildSet.Stub(m => m.GetEnumerator()).Return(children.GetEnumerator());
mockContext.Parents = mockParentSet;
mockContext.Children = mockChildSet;
mockContext.Parents.Should().HaveCount(1);
mockContext.Children.Should().HaveCount(1);
mockContext.Parents.First().Children.FirstOrDefault().Should().NotBeNull();
var query = mockContext.Parents.Include(p=>p.Children).Select(pc => pc);
query.Should().NotBeNull().And.HaveCount(1);
query.First().Children.Should().NotBeEmpty().And.HaveCount(1);
}