Include方法的位置会影响成功或失败

时间:2018-01-09 21:25:51

标签: entity-framework linq unit-testing entity-framework-6 moq

我正在编写单元测试来测试使用Entity Framework和LINQ获取数据的方法。我在测试中使用了模拟的DbSet。该方法使用扩展名Orders方法从DbSet Customers返回数据,以及来自相关DbSet System.Data.Entity.Include的导航属性。

我遇到了一个奇怪的情况,根据我调用Include的位置,A)测试成功或B)我得到一个异常。这就是我的问题。

(这是对实际代码的简化。我意识到这些测试,如下所述,是愚蠢而毫无意义的。)

public class Order
{
    // Primary key
    public string OrderId { get; set; }

    // Foreign key to Customers table
    public string CustomerId { get; set; }

    // Navigation property
    public virtual Customer Customer {get; set; }
}

public class Customer
{
    // Primary key
    public string CustomerId { get; set; }
}

public class MyContext : DbContext
{
    public virtual DbSet<Customer> Customers { get; set; }
    public virtual DbSet<Order> Orders { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>()
            .HasKey(p => new { p.OrderId });

        modelBuilder.Entity<Order>()
            .HasRequired(p => p.Customer)
            .WithMany();

        modelBuilder.Entity<Customer>()
            .HasKey(p => new { p.CustomerId });
    }

    // ...
}

[TestClass]
public class MyTests
{
    // Creates a mock DbSet that can be used for Entity Framework contexts
    private static Mock<DbSet<TEntity>> CreateMockDbSet<TEntity>(IEnumerable<TEntity> models) where TEntity : class
    {
        Mock<DbSet<TEntity>> dbSet = new Mock<DbSet<TEntity>>();

        IQueryable<TEntity> queryable = models.AsQueryable();

        dbSet.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(queryable.ElementType);
        dbSet.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(queryable.Expression);
        dbSet.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(queryable.GetEnumerator());
        dbSet.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(queryable.Provider);

        return dbSet;
    }

    // This test succeeds
    [TestMethod]
    public void GetOrders1()
    {
        Mock<DbSet<Customer>> customersDbSet = CreateMockDbSet(new List<Customer>
        {
            new Customer { CustomerId = "12345" }
        });
        Mock<DbSet<Order>> ordersDbSet = CreateMockDbSet(new List<Order>
        {
            new Order { OrderId = "0000000001", CustomerId = "12345" }
        });
        Mock<MyContext> context = new Mock<MyContext>();
        context.Setup(e => e.Customers).Returns(customersDbSet.Object);
        context.Setup(e => e.Orders).Returns(ordersDbSet.Object);

        // This succeeds
        List<Order> orders =
            (from o in context.Object.Orders
             select o).Include(p => p.Customer).ToList();

        Assert.AreEqual(1, orders.Count);
    }

    // This test results in an exception that says "System.ArgumentNullException: Value cannot be null."
    [TestMethod]
    public void GetOrders2()
    {
        Mock<DbSet<Customer>> customersDbSet = CreateMockDbSet(new List<Customer>
        {
            new Customer { CustomerId = "12345" }
        });
        Mock<DbSet<Order>> ordersDbSet = CreateMockDbSet(new List<Order>
        {
            new Order { OrderId = "0000000001", CustomerId = "12345" }
        });
        Mock<MyContext> context = new Mock<MyContext>();
        context.Setup(e => e.Customers).Returns(customersDbSet.Object);
        context.Setup(e => e.Orders).Returns(ordersDbSet.Object);

        // This fails
        List<Order> orders =
            (from o in context.Object.Orders.Include(p => p.Customer)
             select o).ToList();

        Assert.AreEqual(1, orders.Count);
    }        
}

两个测试是相同的,除了我的LINQ查询中Include方法的位置。我已经使用附加的真实数据库检查了这一点,并且两种编写查询的方式都会导致执行相同的SQL。

为什么第一个测试方法成功,但第二个测试方法会导致异常?

0 个答案:

没有答案