[英]Using Moq, is it possible to setup mocked tables by type?
我有以下代碼,試圖根據傳遞給MockDbSet方法的數據類型來設置模擬表。
private Mock<DbContext> mockContext = new Mock<DbContext>();
public DbContext GetContext()
{
return mockContext.Object;
}
public void MockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
mockContext.Setup(c => c.Set(typeof(T))).Returns(dbSet.Object);
}
我在mockContext.Setup行(22)處收到以下錯誤:
System.NotSupportedException: Conversion between generic and non-generic DbSet objects is not supported for test doubles.
我試過了
mockContext.Setup(c => c.Set<T>()).Returns(dbSet.Object);
這不會引發異常,但是也不會設置任何數據。
是否可以通過這種方式設置表格?
謝謝
要在存儲庫級別概述模擬:
通過合同接口與Repostory交互的給定服務/控制器代碼:
public interface IOrderRepository
{
IQueryable<Order> GetOrderById (int orderId);
IQueryable<Order> GetOrdersForCustomer (int customerId);
}
這是我使用的首選存儲庫模式。 返回IQueryable意味着我的使用者可以利用延遲執行來決定如何使用細節,從而使查詢效率更高。 (即使用.Select()獲取他們想要的字段,執行.Count()或.Any()、. FirstOrDefault()或.Skip()。Take()等)
或者,您可以使用通用存儲庫:
public interface IRepository<Order>
{
Order GetOrderById (int orderId);
ICollection<Order> GetOrdersForCustomer (int customerId);
}
存儲庫方法將包含最少或沒有業務邏輯。 在我的情況下,存儲庫只能與以下內容配合使用:
所有業務邏輯都應駐留在您的服務類或控制器中,可以在其中對其進行單獨測試。 為了測試上述3個條件(如果適用),我使用集成測試。 這些條件是非常低級的檢查和對任何定期不會改變。
假設被測代碼在Controller中。
public class OrderController : IOrderController
{
private readonly IOrderRepository _repository = null;
private readonly IUnitOfWorkFactory _uowFactory = null;
public OrderController(IUnitOfWorkFactory uowFactory, IOrderRepository repository)
{
if (uowFactory == null)
throw new ArgumentNullException("uowFactory");
if (repository == null)
throw new ArgumentNullException("repository");
_uowFactory = uowFactory;
_repository = repository;
}
public void SomeActionOnOrder(int orderId)
{
using (var unitOfWork = _uowFactory.Create())
{
var order = _repository.GetOrderById(orderId)
// Here lies your validation checks that the order was found,
// business logic to do your behaviour.. This is the stuff you want to test..
// ...
unitOfWork.Commit();
}
}
}
現在,當您去測試控制器時...
[Test]
public void EnsureSomeActionOnOrderDoesIt()
{
var uowMock = new Mock<IUnitOfWork>();
var uowFactoryMock = new Mock<IUnitOfWorkFactory>();
var repositoryMock = new Mock<IOrderRepository>();
var testOrderId = -1;
var stubOrders = new [] { newOrder { /* populate expected data... */ } };
uowMock.Setup(x=>x.Commit());
uowFactoryMock.Setup(x=>x.Create()).Returns(uowMock.Object);
repositoryMock.Setup(x=>x.GetOrderById(testOrderId)).Returns(stubOrders.AsQueryable());
var testController = new OrderController(uowFactoryMock.Object, repositoryMock.Object);
testController.SomeActionOnOrder(testOrderId);
// Everything "touched" as expected? (did the code fetch the object? did it save the changes?)
uowFactoryMock.VerifyAll();
uowMock.VerifyAll();
repositoryMock.VerifyAll();
// Perform asserts on your stub order if SomeAction modified state as you expected.
}
針對實際數據庫的集成測試將處理存儲庫應涵蓋的任何邏輯。
我上面的存儲庫模式是IQueryable風格,或者,如果您返回一個實體,只需返回帶有存根順序的“存根”並返回它。
我使用的模擬框架是Moq。 僅基於內存,以上代碼在語法上可能並不完全正確。 :)
直到TDD / BDD為止,單元測試的目標是這些測試應該可靠地可重復且快速執行,以便在開發時可以重復且頻繁地運行它們。 保持存儲庫相對較薄,並且不涉及業務邏輯決策,這意味着它們可以充當單元測試模擬的可靠起點。 存儲庫的工作是返回數據,因此通過進行模擬,這意味着我們可以控制我們希望被測代碼可以使用的數據。 我們可以模擬它以返回對象,null,引發異常,無論我們的測試方案希望我們的被測試代碼處理什么。
在上面的示例中,我還演示了包裝數據庫上下文的基本工作單元模式的使用。 我用於EF的實現是Medhime的DB Context Scope Factory / Locator。 使用工作單元模式,我們還提供了一些模擬,可以驗證被測代碼是否正在(或不)保存數據。 存儲庫需要鏈接到一個工作單元(在構造函數中初始化,或者按照Mehdime模式“定位”),但是在測試我們的服務和控制器時我們並不關心該方面,存儲庫僅僅是模擬出來,其目的是返回和(可選)創建數據。
我將把我的存儲庫用作實體的工廠(即帶有產品詳細信息和數量列表的CreateOrder()),以確保使用所有預期的參考鏈接和所需數據初始化新實體,而不是依賴於調用代碼。 該調用代碼將必須充斥額外的查詢等,以檢索新訂單的參考數據,因此,我將視圖模型數據傳遞到訂單存儲庫以進行解析,連接並返回有效的新訂單。實體。
在最近的項目中,我創建了List<T>
的擴展方法(可以是IEnumerable或其他任何方法)。
public static Mock<DbSet<T>> MockList<T>(this List<T> list) where T: class
{
var mockDbSet = new Mock<DbSet<T>>();
var queryable = list.AsQueryable();
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());
return mockDbSet;
}
調用起來非常簡單。
var myData = new List<MyDataType> { new MyDataType(), new MyDataType(), ....};
var mockDb = new Mock<MyContext>();
mockDb.Setup(x => x.MyDatas).Returns(myData.MockList().Object);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.