实体框架6模拟包括dbset上的方法

21 c# entity-framework-6

一直在谷歌搜索有关如何在EF6中模拟dbset上的include方法的问题的解决方案.问题在这里有详细记载: -

http://entityframework.codeplex.com/discussions/461731

不幸的是,虽然似乎没有一个有效的解决方案.

有人找到了解决方法吗?

我确实理解我们不应该真的嘲笑EF6环境,但项目负责人坚持要求.

提前致谢.

Cli*_*int 24

我和上面的@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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的不喜欢必须有两个假DbSets的笨拙但由于某种原因这不起作用:

parentsDbSet1.Setup(x => x.Include(It.IsAny<string>())).Returns(parentsDbSet1.Object);
Run Code Online (Sandbox Code Playgroud)

有人对此有解释吗?

  • `set.Setup(s => s.Include(It.IsAny <string>())).返回(set.Object);`适合我. (9认同)
  • 我不确定,但是`s.Include(It.IsAny <string>())`**在使用lambdas时也可以工作.奖励! (4认同)
  • 我还在`FakeDbSet <T>`构造函数中添加以下内容作为最后一行,这样我就不需要在所有测试中继续重复:`Setup(m => m.Include(It.IsAny <string> ())).返回(对象);`我目前有很多单元测试,Moq使用该方法成功运行. (2认同)

Pau*_*bra 13

所以,如果有点冒险,这是可能的!

在下面我设置模拟上下文和设置,并可以成功调用包含.我认为秘诀就是将调用存根到Provider,Expression和GetEnumerator,并将存根上下文中的DbSet属性设置为存根集,而不是将上下文存根到返回它们.

GitHub上提供了一个可运行的示例

    [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);

    }
Run Code Online (Sandbox Code Playgroud)


Ped*_*Kid 9

使用Moq框架,这个方法适用于我抛出的所有内容.

    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;
    }
Run Code Online (Sandbox Code Playgroud)

使用它只是做:

    mockContext.Setup(p => p.<DbSetToMock>).Returns(<observableCollection to use as data>.GetMockSet().Object);`
Run Code Online (Sandbox Code Playgroud)

如果上下文实现了一个接口,那么这就好了,因为你永远不需要对EF做任何事情.

编辑:

额外位的原因是我们可以在测试中检查结果,如果我们添加或删除,我们可以检查传递的集合,它将在测试后得到结果.

  • 这对我有用 - 它避免了`NullReferenceException`.我缺少的是我认为我需要模拟`Include(Expresssion <Func <T,TProperty >>)`重载 - 你不能,因为它是一个扩展方法 - 实际上你只需要嘲笑核心`包含(字符串)`方法使事情有效. (4认同)