2017-01-26 54 views
2

我正在使用實體框架,並有一個通用的存儲庫方法,允許我查詢DbSet並且還包括導航屬性。我正在嘗試爲使用這段代碼的代碼編寫一個單元測試,我需要模擬它進行單元測試。我正在使用Moq。Moq - 嘲諷複雜的存儲庫方法 - 不返回列表對象

這是存儲庫方法 - 它是一種方法,允許我使用表達式進行查詢,並且還包括我想要的相關導航屬性。我在Pluralsight的Enterprise課程中看到了Julie Lerman的EF模式。

public IEnumerable<TEntity> FindByInclude(Expression<Func<TEntity, bool>> predicate, 
              params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    var query = GetAllIncluding(includeProperties); 
    IEnumerable<TEntity> results = query.Where(predicate).ToList(); 
    return results; 
} 

private IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties) 
{ 
    IQueryable<TEntity> queryable = DbSet.AsNoTracking(); 

    return includeProperties.Aggregate 
     (queryable, (current, includeProperty) => current.Include(includeProperty)); 
} 

這裏是我如何在我的代碼使用這種方法調用的一個例子(我只是顯示該方法的相關部分):

public ApiResult DeleteLocation(int id) 
{ 
    var location = _locationRepository 
     .FindByInclude(l => l.Id == id, l => l.LocationRegions, l => l.Pools) 
     .Single(); 

所以這個查詢會帶回一個單Location實體由我通過的ID和相關的LocationRoomsStaff收藏。

如何設置Moq爲FindByInclude方法?以下是我對我的單元測試模擬設置:

var mockLocationRepository = new Mock<ILocationRepository>(); 
var location = new Location {Id = 1,Name = "LocationName", LocationRooms = new List<LocationRoom>(), Staff = new List<Staff>()}; 
mockLocationRepository.Setup(r => r.FindByInclude(l => l.Id == It.IsAny<int>(), l => l.LocationRooms, l => l.Staff)) 
      .Returns(() => new List<Location> { location }); 

從這裏顯示的起訂量設置代碼 - 我想我應該回去了1個位置的列表 - 我ID爲1所指定的位置對象。但是,當我運行我的單元測試,並打這個代碼 - FindByInclude的設置方法返回一個空列表。因此,當DeleteLocation方法中的代碼被擊中並且調用Single()方法時,我收到「元素不包含序列」的錯誤。

我認爲問題是我的Moq設置爲FindByInclude方法的語法有問題,但不知道什麼是錯的。

回答

2

太長的評論,所以添加爲答案

它是表達式。首先嚐試更一般的表達式設置,看看它是否有效。

var location = new Location { 
    Id = 1, 
    Name = "LocationName", 
    LocationRooms = new List<LocationRoom>(), 
    Staff = new List<Staff>() 
}; 
mockLocationRepository 
    .Setup(m => m.FindByInclude(It.IsAny<Expression<Func<TEntity, bool>>>(), It.IsAny<Expression<Func<TEntity, object>>[]>()) 
    .Returns(() => new List<Location> { location }); 
+0

Thanks Nkosi - 這正是我需要:) – user1750537

4

作爲替代@恩科西的回答,你怎麼樣不使用起訂量,但其實現自己的ILocationRepository存根實現?這背後的想法是,如果嘲笑變得難以做到,也許你不應該這樣做?

public class StubLocationRepository : ILocationRepository 
{ 
    private readonly IEnumerable<Location> _findByInclude; 

    public StubLocationRepository(IEnumerable<Location> findByInclude) 
    { 
     _findByInclude = findByInclude; 
    } 

    public IEnumerable<Location> FindByInclude(
     Expression<Func<Location, bool>> predicate, 
     params Expression<Func<Location, object>>[] includeProperties) 
    { 
     return _findByInclude; 
    } 
} 

這很簡單,因爲它假設你只有一種方法。如果您有很多並且不想爲每個參數傳遞常量值,那麼可以讓存根的ctor採用可選參數,因此您只需要存儲所需的方法。

此外,由於ILocationRepository最有可能繼承自通用接口,所以您可以擁有一個通用的存根實現,該子實現可以繼承以構建特定的存根 - 即實現ILocationRepository定義的方法。

+0

偉大的建議 - 非常感謝 - 我將記住這對未來的時候,當我卡住 - 標記Nkosi的答案是正確的,因爲它更快實施 - 我只是有一些語法錯誤。乾杯 – user1750537

+1

我也喜歡這個想法,並同意難度口頭禪。 – Nkosi

+0

@Nkosi謝謝,修正。 –