2015-11-10 19 views
5

我使用moq,ef 6和xunit。我發現自己一遍又一遍地寫這些代碼,並認爲我可以把它變成一個通用的方法,但有一些麻煩。如何將此EF Mock設置代碼編寫爲可重用的通用Boilerplate?

public static void CreateSalesMock(List<Sale> sales, Mock<DatabaseContext> dbContextMock) 
{ 
    var data = sales.AsQueryable(); 

    var mockSet = new Mock<DbSet<Sale>>(); 
    mockSet.As<IQueryable<Sale>>() 
      .Setup(x => x.Provider) 
      .Returns(data.Provider); 
    mockSet.As<IQueryable<Sale>>() 
      .Setup(x => x.Expression) 
      .Returns(data.Expression); 
    mockSet.As<IQueryable<Sale>>() 
      .Setup(x => x.ElementType) 
      .Returns(data.ElementType); 
    mockSet.As<IQueryable<Sale>>() 
      .Setup(x => x.GetEnumerator()) 
      .Returns(data.GetEnumerator()); 

    dbContextMock.Setup(x => x.Sales).Returns(mockSet.Object); 
} 

現在我有很多其他表在我的數據庫,所以如果我能寫,將採取在數據列表和設定,讓我可以模擬查詢通過這將是巨大的一種方法。

public static void CreateMockSet<T, TA, TB>(T dataList, TA model, 
    Func<TB> lambda, Mock<DatabaseContext> dbContextMock) 
    where T : List<T> 
    where TA: Mock<DbSet<TA>> 
{ 
    var data = dataList.AsQueryable(); 

    model.As<IQueryable<T>>() 
     .Setup(x => x.Provider) 
     .Returns(data.Provider); 
    model.As<IQueryable<T>>() 
     .Setup(x => x.Expression) 
     .Returns(data.Expression); 
    model.As<IQueryable<T>>() 
     .Setup(x => x.ElementType) 
     .Returns(data.ElementType); 
    model.As<IQueryable<T>>() 
     .Setup(x => x.GetEnumerator()) 
     .Returns(data.GetEnumerator()); 

    dbContextMock.Setup(x => lambda); 
} 

到目前爲止,我有,但我不知道如果這將工作與否。我被卡住在「lambda」部分(例如x => x.Sales),所以我甚至無法測試它。在UT

public static class DbSetMocking 
{ 
    private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data) 
      where T : class 
    { 
     var queryableData = data.AsQueryable(); 
     var mockSet = new Mock<DbSet<T>>(); 
     mockSet.As<IQueryable<T>>().Setup(m => m.Provider) 
       .Returns(queryableData.Provider); 
     mockSet.As<IQueryable<T>>().Setup(m => m.Expression) 
       .Returns(queryableData.Expression); 
     mockSet.As<IQueryable<T>>().Setup(m => m.ElementType) 
       .Returns(queryableData.ElementType); 
     mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()) 
       .Returns(queryableData.GetEnumerator()); 
     return mockSet; 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
      this IReturns<TContext, DbSet<TEntity>> setup, 
      TEntity[] entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     Mock<DbSet<TEntity>> mockSet; 
     return ReturnsDbSet(setup, entities, out mockSet); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
      this IReturns<TContext, DbSet<TEntity>> setup, 
      IQueryable<TEntity> entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 

     Mock<DbSet<TEntity>> mockSet; 
     return ReturnsDbSet(setup, entities, out mockSet); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
      this IReturns<TContext, DbSet<TEntity>> setup, 
      IEnumerable<TEntity> entities) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     Mock<DbSet<TEntity>> mockSet; 
     return ReturnsDbSet(setup, entities, out mockSet); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
    this IReturns<TContext, DbSet<TEntity>> setup, 
    TEntity[] entities, out Mock<DbSet<TEntity>> mockSet) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     mockSet = CreateMockSet(entities.AsQueryable()); 
     return setup.Returns(mockSet.Object); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
      this IReturns<TContext, DbSet<TEntity>> setup, 
      IQueryable<TEntity> entities, out Mock<DbSet<TEntity>> mockSet) 
     where TEntity : class 
     where TContext : DbContext 
    { 

     mockSet = CreateMockSet(entities); 
     return setup.Returns(mockSet.Object); 
    } 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(
    this IReturns<TContext, DbSet<TEntity>> setup, 
    IEnumerable<TEntity> entities, out Mock<DbSet<TEntity>> mockSet) 
     where TEntity : class 
     where TContext : DbContext 
    { 
     mockSet = CreateMockSet(entities.AsQueryable()); 
     return setup.Returns(mockSet.Object); 
    } 

} 

然後你使用它,如下所示::

+0

我想你想改變'where'約束引入一個接口:'其中T:列表'。 –

+0

我建議看看Builder模式請參閱https://www.kenneth-truyers.net/2013/07/15/flexible-and-expressive-unit-tests-with-the-builder-pattern/ –

+0

@ ToddSprang我不確定IBase會是什麼。你可以舉更多的例子 – chobo2

回答

9

蒂姆·拉爾森已經提供了在his blog這個樣板代碼的最佳解決方案

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

編輯

我更新了代碼。現在有3個重載允許在DbSet<T>財產覈查:

[TestMethod] 
    public void TestMethod1() 
    { 
     var sales = new List<Sale> 
     { 
      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<SalesContext>(); 
     Mock<DbSet<Sale>> 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)); 

    } 
+0

酷,這是我正在尋找,但如果我需要添加更多的設置到Mockset會發生什麼?例如,我想查看RemoveRange是否在我剛剛在SalesMock上運行驗證並調用之前被調用。但現在我沒有它,我不知道該怎麼做。我試圖通過dbContext.Verify(x => x.Sales.RemoveRange(...))進行驗證,但總是失敗。 – chobo2

+0

@ chobo2不要做這樣的事情。 'context.Sales'是'Sales'的一個List,它是一個BCL集合。你不應該嘲笑BCL系列。請確保列表不再包含元素。 –

+0

如何測試列表不再包含元素?我先試過,但我的單元測試方面似乎從未將它們移除。就像在我的服務中,我看到他們被刪除,但當我看着我的原始集合,他們仍然在那裏。 – chobo2