1

我正在使用 .NET 4.5、EF6,并且正在尝试使用 JustMock 2.0 来测试我的应用程序。

我试图通过模拟我的DbContext子类来模拟我的数据库:CoreDataRepositoryContext

为此,我需要通过返回一个假数据集合来模拟DbContext的成员SaveChanges和我的类CoreDataRepositoryContext的每个DbSet类型属性。我还需要模拟以下DbSet的成员:

  • 添加
  • 消除
  • 可查询的

我需要为CoreDataRepositoryContextDbSet的所有实例模拟它

例如,我在数据库中有Order类型的实体(表Orders)我做了以下模拟表Orders

// FakeOrders is a list of orders (List<Order>)

var mockedContext = Mock.Create<CoreDataRepositoryContext>();

   // Mock works
Mock.Arrange(() => mockedContext.SaveChanges()).IgnoreInstance().DoNothing();
   // Mock works
Mock.Arrange(() => mockedContext.Orders).IgnoreInstance().ReturnsCollection(FakeOrders);
   // Mock works
Mock.Arrange(() => mockedContext.Orders.Add(Arg.IsAny<Order>())).IgnoreInstance().DoInstead((Order o) => FakeOrders.Add(o));
   // Mock works
Mock.Arrange(() => mockedContext.Orders.Remove(Arg.IsAny<Order>())).IgnoreInstance().DoInstead((Order o) => FakeOrders.Remove(o));
   // Mock DOES NOT work !
Mock.Arrange(() => mockedContext.Orders.AsQueryable()).IgnoreInstance().Returns(() => FakeOrders.AsQueryable());

mockedContext.Orders属于DbSet< Order >类型,而 FakeOrders 属于List< Order >类型。这两个类都实现了接口IEnumerable< Order >

模拟AddRemove成员效果很好,因为这两种方法都没有在接口IEnumerable< T >中声明。

另一方面,AsQueryable在此接口中声明并由Queryable定义。因此,当我使用IgnoreInstance模拟成员时,从实现IEnumerable< T >的任何类的实例调用AsQueryable 会启动一个永无止境的循环。因为IEnumerable< Order >.AsQueryableFakeOrders.AsQueryable嘲笑,而 FakeOrders.AsQueryable被... FakeOrders.AsQueryable ...无限循环...

var query = mockedContext.Orders.AsQueryable(); // Infinite loop
query = FakeOrders.AsQueryable(); // Infinite loop
query = new List<Order>().AsQueryable(); // Infinite loop

我怎样才能只模拟DbSet< Order >.AsQueryable,而不使用IgnoreInstance 模拟IEnumerable< Order > .AsQueryable

感谢您的帮助 :)

4

2 回答 2

0

您可以IgnoreInstance()从最后的安排中删除。这将使它仅在对该Orders属性调用时才起作用。

尽管从技术上讲,您不需要在Orders属性上安排任何这些方法。ReturnsCollection将注意将IList<T>所有IQueryable<T>方法代理到目标(如果目标实现了这些接口中的任何一个)。由于FakeOrders是一个 List,thenAddRemoveonOrders将被代理到支持集合上的相同方法。AsQueryable不是实例方法,而是扩展方法。因此,您不能为特定的派生类型模拟它,但您甚至不需要这样做。的原始实现AsQueryable足够灵活,因此您根本不需要模拟它。

如果您只是删除最后三个排列,您的测试仍将按预期工作。

于 2014-12-03T10:12:58.440 回答
0

好的,我终于找到了解决方法:

我没有安排 DbContext ,但是当使用特定的连接字符串作为参数调用时,IgnoreInstance我安排了我的子类的构造函数:DbContext

string myConnectionString = "CoreDBTestConnection";

// Arrange all CoreDataRepositoryContext instances for this connection string
Mock.Arrange(() => new CoreDataRepositoryContext(myConnectionString).Returns(GetMockContext());

构造函数被替换为GetMockContext()重新运行模拟上下文:

private static CoreDataRepositoryContext GetMockContext()
{
        CoreDataRepositoryContext mockContext = new CoreDataRepositoryContext();

        Mock.Arrange(() => mockContext.SaveChanges()).DoNothing();

        Mock.Arrange(() => mockContext.Set<Order>()).ReturnsCollection(FakeOrders);
        Mock.Arrange(() => mockContext.Orders).ReturnsCollection(FakeOrders);
        Mock.Arrange(() => mockContext.Orders.Add(Arg.IsAny<Order>())).DoInstead((Order o) => FakeOrders.Add(o));
        Mock.Arrange(() => mockContext.Orders.Remove(Arg.IsAny<Order>())).DoInstead((Order o) => FakeOrders.Remove(o));
        Mock.Arrange(() => mockContext.Orders.Find(Arg.IsAny<object[]>())).Returns((object[] param) => FakeOrders.Find(x => x.Id == (Guid)param[0]));
        Mock.Arrange(() => mockContext.Orders.AsQueryable()).Returns(() => FakeOrders.AsQueryable());

        // Then arrange all DbSets...

        return mockContext;
}

public static FakeDataList<Order> FakeOrders = new List<Order>();
于 2014-12-08T17:02:24.000 回答