如何将此EF Mock设置代码编写为可重用的Generic Boilerplate?

我正在使用moq,ef 6和xunit。 我发现自己一遍又一遍地编写这段代码,并且想到也许我可以把它变成一个通用的方法,但是遇到了一些麻烦。

public static void CreateSalesMock(List sales, Mock dbContextMock) { var data = sales.AsQueryable(); var mockSet = new Mock<DbSet>(); mockSet.As<IQueryable>() .Setup(x => x.Provider) .Returns(data.Provider); mockSet.As<IQueryable>() .Setup(x => x.Expression) .Returns(data.Expression); mockSet.As<IQueryable>() .Setup(x => x.ElementType) .Returns(data.ElementType); mockSet.As<IQueryable>() .Setup(x => x.GetEnumerator()) .Returns(data.GetEnumerator()); dbContextMock.Setup(x => x.Sales).Returns(mockSet.Object); } 

现在我在我的数据库中有许多其他表,所以如果我可以编写一个方法来接收该数据的列表并进行设置,那么我可以通过它进行模拟查询会很棒。

 public static void CreateMockSet(T dataList, TA model, Func lambda, Mock dbContextMock) where T : List where TA: Mock<DbSet> { var data = dataList.AsQueryable(); model.As<IQueryable>() .Setup(x => x.Provider) .Returns(data.Provider); model.As<IQueryable>() .Setup(x => x.Expression) .Returns(data.Expression); model.As<IQueryable>() .Setup(x => x.ElementType) .Returns(data.ElementType); model.As<IQueryable>() .Setup(x => x.GetEnumerator()) .Returns(data.GetEnumerator()); dbContextMock.Setup(x => lambda); } 

到目前为止,我有这个,但我不确定这是否有效。 我被困在传递“lambda”部分(例如x => x.Sales ),所以我甚至无法测试它。

Tim Larson已经在他的博客中为这个样板代码提供了一个很好的解决方案:

 public static class DbSetMocking { private static Mock> CreateMockSet(IQueryable data) where T : class { var queryableData = data.AsQueryable(); var mockSet = new Mock>(); mockSet.As>().Setup(m => m.Provider) .Returns(queryableData.Provider); mockSet.As>().Setup(m => m.Expression) .Returns(queryableData.Expression); mockSet.As>().Setup(m => m.ElementType) .Returns(queryableData.ElementType); mockSet.As>().Setup(m => m.GetEnumerator()) .Returns(queryableData.GetEnumerator()); return mockSet; } public static IReturnsResult ReturnsDbSet( this IReturns> setup, TEntity[] entities) where TEntity : class where TContext : DbContext { Mock> mockSet; return ReturnsDbSet(setup, entities, out mockSet); } public static IReturnsResult ReturnsDbSet( this IReturns> setup, IQueryable entities) where TEntity : class where TContext : DbContext { Mock> mockSet; return ReturnsDbSet(setup, entities, out mockSet); } public static IReturnsResult ReturnsDbSet( this IReturns> setup, IEnumerable entities) where TEntity : class where TContext : DbContext { Mock> mockSet; return ReturnsDbSet(setup, entities, out mockSet); } public static IReturnsResult ReturnsDbSet( this IReturns> setup, TEntity[] entities, out Mock> mockSet) where TEntity : class where TContext : DbContext { mockSet = CreateMockSet(entities.AsQueryable()); return setup.Returns(mockSet.Object); } public static IReturnsResult ReturnsDbSet( this IReturns> setup, IQueryable entities, out Mock> mockSet) where TEntity : class where TContext : DbContext { mockSet = CreateMockSet(entities); return setup.Returns(mockSet.Object); } public static IReturnsResult ReturnsDbSet( this IReturns> setup, IEnumerable entities, out Mock> mockSet) where TEntity : class where TContext : DbContext { mockSet = CreateMockSet(entities.AsQueryable()); return setup.Returns(mockSet.Object); } } 

然后在UT中,您将其用作以下内容:

 var context = new Mock(); context.setup(x => x.Sales).ReturnsDbSet(new List(){put here the items..}); 

编辑

我更新了代码。 现在还有3个重载允许validationDbSet属性:

  [TestMethod] public void TestMethod1() { var sales = new List { new Sale() {id = 1}, new Sale() {id = 6}, new Sale() {id = 5}, new Sale() {id = 4}, new Sale() {id = 3}, new Sale() {id = 2} }; var fakeContest = new Mock(); Mock> fakeSet; fakeContest.Setup(context => context.Sales).ReturnsDbSet(sales, out fakeSet); var itemsToRemove = sales.Where(sale => sale.id%2 == 0); fakeContest.Object.Sales.RemoveRange(itemsToRemove); fakeSet.Verify(set => set.RemoveRange(itemsToRemove)); }