2016-10-04 93 views
3

我目前使用的擴展方法一般模擬DbSets作爲一個列表:有沒有辦法一般地用Moq模擬DbSet.Find方法?

public static DbSet<T> AsDbSet<T>(this List<T> sourceList) where T : class 
    { 
     var queryable = sourceList.AsQueryable(); 
     var mockDbSet = new Mock<DbSet<T>>(); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType); 
     mockDbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); 
     mockDbSet.Setup(x => x.Add(It.IsAny<T>())).Callback<T>(sourceList.Add); 
     mockDbSet.Setup(x => x.Remove(It.IsAny<T>())).Returns<T>(x => { if (sourceList.Remove(x)) return x; else return null; }); 

     return mockDbSet.Object; 
    } 

但是,我不能想出一個辦法來嘲笑查找方法,它需要查詢的表的主鍵。我可以在每個表的特定級別執行此操作,因爲我可以檢查數據庫,獲取PK,然後模擬該字段的Find方法。但是,我不能使用通用方法。

我想我也可以添加到EF自動生成的部分類中,以標記哪個字段是具有屬性或某物的PK。但是我們有超過100個表格,如果您依靠人員來手動維護,那麼代碼會更難管理。

EF6是否提供了查找主鍵的任何方式,還是隻在連接到數據庫後才動態地知道?

+2

你在找這樣的東西:[link](http://stackoverflow.com/a/25199983/5048049)否則你可以更好地解釋你在找什麼? – peval27

+1

這是我描述的第一個選擇。這只是針對ActiveLoan集合做出特定的模擬,而不是針對任何通用集合的通用模擬。 –

回答

4

經過一段時間的思考,我想我已經找到了目前可用的「最佳」解決方案。我只是有一系列if語句直接檢查擴展方法中的類型。然後我轉換爲我需要的類型來設置查找行爲,並在完成後將其轉換回通用類型。這只是僞通用的,但我想不出更好的。

 if (typeof(T) == typeof(MyFirstSet)) 
     { 
      mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MyFirstSet>).FirstOrDefault(y => y.MyFirstSetKey == (Guid)x[0]) as T); 
     } 
     else if (typeof(T) == typeof(MySecondSet)) 
     { 
      mockDbSet.Setup(x => x.Find(It.IsAny<object[]>())).Returns<object[]>(x => (sourceList as List<MySecondSet>).FirstOrDefault(y => y.MySecondSetKey == (Guid)x[0]) as T); 
     } 
     ...  
+0

不錯。由於我所有的實體都具有相同的主鍵「Id」,因此我可以通過將「y.MySetKey」替換爲「y.GetType()。GetProperty(」Id「).GetValue(y)」來使這更通用。 – Jared

1

據我所知,這個問題沒有「最佳實踐」的答案,但這是我如何接近它。我爲AsDbSet方法添加了一個可選參數,用於標識主鍵,然後可以輕鬆地對Find方法進行模擬。

public static DbSet<T> AsDbSet<T>(this List<T> sourceList, Func<T, object> primaryKey = null) where T : class 
{ 
    //all your other stuff still goes here 

    if (primaryKey != null) 
    { 
     mockSet.Setup(set => set.Find(It.IsAny<object[]>())).Returns((object[] input) => sourceList.SingleOrDefault(x => (Guid)primaryKey(x) == (Guid)input.First())); 
    } 

    ... 
} 

我寫這在一個單一的GUID的假設被用來作爲主鍵這似乎是你如何工作的,但如果你需要更多的靈活性原則應該是很容易適應複合鍵等

0

我在下面的類結束:

public static class DbSetMocking 
{ 
    #region methods 

    public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, ICollection<TEntity> entities, Func<object[], TEntity> find = null) 
     where TEntity : class where TContext : DbContext 
    { 
     return setup.Returns(CreateMockSet(entities, find).Object); 
    } 

    private static Mock<DbSet<T>> CreateMockSet<T>(ICollection<T> data, Func<object[], T> find) 
     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()); 

     mockSet.SetupData(data, find); 

     return mockSet; 
    } 

    #endregion 
} 

哪個可以用於:

private static MyRepository SetupRepository(ICollection<Type1> type1s, ICollection<Type2> type2s) 
{ 
    var mockContext = new Mock<MyDbContext>(); 

    mockContext.Setup(x => x.Type1s).ReturnsDbSet(type1s, o => type1s.SingleOrDefault(s => s.Secret == (Guid) o[ 0 ])); 
    mockContext.Setup(x => x.Type2s).ReturnsDbSet(type2s, o => type2s.SingleOrDefault(s => s.Id == (int) o[ 0 ])); 

    return new MyRepository(mockContext.Object); 
} 
相關問題