我正在编写单元测试来测试使用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。
为什么第一个测试方法成功,但第二个测试方法会导致异常?