模拟实体框架内连接

时间:2014-03-26 12:13:09

标签: entity-framework mocking moq

我需要从我的数据访问层单元测试内连接方法。

MY DAL看起来像这样:

public class MyDAL:IMyDAL
{
    private MyContext context;

public MyDAL(MyContext Context)
    {
        this.context = Context;
    }
public IEnumerable <Parent> Read(int childId)
    {
        var query = from parent in context.Parent
                    join child in context.Child on parent.Id equals child.Id
                    where child.Id == childId
                    select env;
        return query.ToList();
    }

我希望通过模拟Entity框架对Read方法进行单元测试,并遵循此链接http://msdn.microsoft.com/en-us/data/dn314429.aspx

  [TestMethod]
    public void ReadMethod()
    {

        var data = new List<Parent> 
        { 
            new Parent { Id = 20 },
        }.AsQueryable();
        var data2 = new List<Child> 
        { 
            new Child { Id = 8 },
        }.AsQueryable();


        var mockSetPar = new Mock<DbSet<Parent>>();
        var mockSetChild = new Mock<DbSet<Child>>();

        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSetPar.As<IQueryable<Parent>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        moockSetChild.As<IQueryable<Child>>().Setup(m => m.Provider).Returns(data2.Provider);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.Expression).Returns(data2.Expression);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.ElementType).Returns(data2.ElementType);
        mockSetChild.As<IQueryable<Child>>().Setup(m => m.GetEnumerator()).Returns(data2.GetEnumerator());


        var customDbContextMock = new Mock<MyContext>();
        customDbContextMock.Setup(x => x.Parent).Returns(mockSetPar.Object);
        customDbContextMock.Setup(x => x.Child).Returns(mockSetChild.Object);




        var myDAL = new MyDAL(customDbContextMock.Object);


        var actual = myDAL.Read(8);
          Assert.IsNotNull(actual);

结果实际为空,因为尚未模拟连接方法,因此它不返回任何内容。

如何模拟join方法以返回值?

谢谢

2 个答案:

答案 0 :(得分:2)

我想您已经注意到,模拟EF查询既耗时又脆弱。我的建议 - 不要嘲笑它。您可以在存储库接口下隐藏尽可能多的数据访问逻辑,这很容易模拟:

public interface IParentRepository
{
    IEnumerable<Parent> GetParentsOfChild(int childId);
}

然后测试看起来像:

[TestMethod]
public void ReadMethod()
{
    int childId = // get sample id
    var expected = // get sample parents

    var repositoryMock = new Mock<IParentRepository>();
    repositoryMock.Setup(r => r.GetParentsOfChild(childId))
                  .Returns(expected);

    var myDAL = new MyDAL(repositoryMock.Object);
    var actual = myDAL.Read(childId);

    repositoryMock.VerifyAll();
    CollectionAssert.AreEqual(actual, expected);
}

如果要验证查询实现,那么最好的方法是接受/集成测试,它涉及真正的数据库。请记住 - 分析生成的IQueryable不足以确保您的查询在真实环境中有效。例如。您可以将运算符Last()与IQueryable一起使用,但此查询将无法由实体框架转换为SQL。

答案 1 :(得分:2)

数据库交互的内存中测试可能会产生误导,因为LINQ to Entities功能是LINQ to Objects功能的子集,因此您可以编写一个绿色的测试,但查询将始终在生产中引发异常。 / p>

这在单一概念层面上也是错误的。 DAL是一个代码,它存在于两个系统的边界上 - app和db。它的责任是整合它们。因此,如果您将这些集成组件隔离开来,那么您的测试将变得毫无意义,因为它会削弱SUT的核心职责。

要测试查询逻辑,您必须使用与生产类似的数据库提供程序。所以你需要集成测试。 =一个有用的解决方案是使用SQLite内存数据库。它将在实体框架涵盖的大多数场景中表现为真实数据库,但其性能几乎与单元测试中基于内存中集合的模拟一样快。

如果您愿意,可以将SQLite视为类固醇的数据库模拟。

相关问题