2016-09-19 87 views
1

如何在對象上設置linq擴展的位置? DbSet在我的情況。這裏是我的代碼:Moq - setup在哪裏擴展?

this.workflowStateSet 
    .Setup(m => m.Where(It.IsAny<Expression<Func<Model.WorkflowState, int, bool>>>())) 
    .Returns(new List<Model.WorkflowState>().AsQueryable()); 

但是,它給了我的異常不是很熟悉例外:

System.NotSupportedException:表達式引用 不屬於嘲笑對象的方法:m => m.Where<WorkflowState>

我將不勝感激任何提示。

+2

你想模擬Linq-methods?非常可怕,因爲它們都是靜態的和通用的。爲什麼需要這個?你不能爲你的測試創建一些虛擬的元素序列嗎? – HimBromBeere

+0

是的,你不能。擴展方法是靜態方法周圍的語法糖,並且不能覆蓋靜態方法。 – Will

+0

@會 - 我不嘲笑他們,我想設置他們。那麼,我不知道它是如何工作的,但安裝程序會爲給定的方法創建類似於裝飾器的東西,對吧?所以你可以設置方法調用的結果而不用調用它。這不正確嗎? – Qerts

回答

2

這個擴展方法將幫助模擬的DbSet

public static class MockDbSetExtensions { 
    public static Mock<DbSet<T>> AsDbSetMock<T>(this IEnumerable<T> list) where T : class { 
     IQueryable<T> queryableList = list.AsQueryable(); 
     Mock<DbSet<T>> dbSetMock = new Mock<DbSet<T>>(); 
     dbSetMock.As<IQueryable<T>>().Setup(x => x.Provider).Returns(queryableList.Provider); 
     dbSetMock.As<IQueryable<T>>().Setup(x => x.Expression).Returns(queryableList.Expression); 
     dbSetMock.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(queryableList.ElementType); 
     dbSetMock.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(() => queryableList.GetEnumerator()); 
     return dbSetMock; 
    } 
} 

而且你可以使用它像這樣。

//Arrange 
var data = new List<Model.WorkflowState>(); 
//you would populate your list as needed. 
//convert it to a mock DbSet that uses the list as its datasource 
var workflowStateSet = data.AsDbSetMock(); 
var dbSet = workflowStateSet.Object; 

//Act 
var items = dbSet.Where("Your expression here"); 

//Assert 
//.... 
1

使用存儲庫模式爲數據檢索添加一個抽象層。這個抽象可以被嘲弄。

如果,例如,您試圖檢索所有與STATEID等於1時,工作流程,然後,而不是調用像這樣

var result = DbSet.WorkflowState.Where(w => w.stateId == 1); 

移動這個代碼到另一個類,然後創建一個接口的方法簽名。

public interface IWorkflowStateSetRepository{ 

    IQueryable<Model.WorkflowState> GetAllWorkflows(int state); 
} 

實施

public class WorkflowStateSetRepository : IWorkflowStateSetRepository{ 

    public IQueryable<Model.WorkflowState> GetAllWorkflows(int state){ 
     return DbSet.WorkflowState .Where(w => w.stateId == state); 
    } 
} 

調用代碼得到IWorkflowStateSetRepository(可能是從你的IoC容器)的實例,並調用GetAllWorkflows()方法來代替。這會給你和以前相同的結果,但你現在可以在你的測試中模擬接口,並設置對這些方法的調用。

this.MockedIWorkflowStateSetRepository.Setup(m => m.GetAllWorkflows(It.IsAny<int>())) 
     .Returns(new List<Model.WorkflowState>().AsQueryable()); 

此代碼是更容易維護和(有適當命名的變量和方法)也傳達意圖好了很多。


儲存庫圖案在這裏更詳細地討論;

http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

+0

鏈接需要發佈爲評論,而不是答案,在這種情況下你的答案是什麼? –

+0

我們不喜歡SO上的僅鏈接答案。取而代之的是提取這個問題感興趣的部分。 – HimBromBeere

1

你們是不是來模仿真實的DbSet實例?因爲這將不起作用,因爲錯誤消息試圖解釋你。爲了模擬一個類型,它必須是一個接口或者具有虛擬成員(抽象成員也是虛擬的)。

你可以嘗試小樣IDbSet或創建一個自定義DbSet類,比如像下面的類

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Data.Entity; 
using System.Linq; 
using System.Linq.Expressions; 

public class DbSetMock<T> : DbSet<T>, IDbSet<T> 
    where T : class 
{ 
    private readonly ICollection<T> _contentCollection; 

    public DbSetMock(IList<T> contentCollection = null) 
    { 
     _contentCollection = new Collection<T>(contentCollection ?? new List<T>()); 
     AddedEntities = new List<T>(); 
     RemovedEntities = new List<T>(); 
     AttachedEntities = new List<T>(); 
    } 

    public void OverrideContentCollection(IEnumerable<T> newData) 
    { 
     _contentCollection.Clear(); 
     _contentCollection.AddRange(newData); 
    } 

    public IList<T> AddedEntities { get; private set; } 

    public IList<T> AttachedEntities { get; private set; } 

    public override ObservableCollection<T> Local 
    { 
     get 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public IList<T> RemovedEntities { get; private set; } 

    public Type ElementType 
    { 
     get 
     { 
      return typeof(T); 
     } 
    } 

    public Expression Expression 
    { 
     get 
     { 
      return _contentCollection.AsQueryable().Expression; 
     } 
    } 

    public IQueryProvider Provider 
    { 
     get 
     { 
      return _contentCollection.AsQueryable().Provider; 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _contentCollection.GetEnumerator(); 
    } 

    public override T Add(T entity) 
    { 
     AddedEntities.Add(entity); 
     _contentCollection.Add(entity); 
     return entity; 
    } 

    public override T Attach(T entity) 
    { 
     AttachedEntities.Add(entity); 

     var matchingEntity = _contentCollection.SingleOrDefault(x => x.Id == entity.Id); 
     if (matchingEntity != null) 
     { 
      _contentCollection.Remove(matchingEntity); 
     } 

     _contentCollection.Add(entity); 

     return entity; 
    } 

    public override TDerivedEntity Create<TDerivedEntity>() 
    { 
     throw new NotImplementedException(); 
    } 

    public override T Create() 
    { 
     throw new NotImplementedException(); 
    } 

    public override T Find(params object[] keyValues) 
    { 
     throw new NotImplementedException(); 
    } 

    public override T Remove(T entity) 
    { 
     RemovedEntities.Add(entity); 
     _contentCollection.Remove(entity); 
     return entity; 
    } 
} 

可以使用構造函數參數將由dB設置檢索設置內容。

希望這會有所幫助。