[英]Is this a bad Moq setup or a deficiency in Moq?
我試圖測試以下代碼:
public async Task<Activity> Get(long ID, Recruiter User, bool IsArchived = false)
{
Activity result = await collection.FirstOrDefault(x => x.ID == ID && x.Recruiter.CompanyID == User.CompanyID && (!x.Archived || IsArchived));
return result;
}
通過以下測試:
[TestMethod]
public async Task GetDoesThings()
{
long ID = 1;
bool IsArchived = false;
Recruiter User = new Recruiter()
{
CompanyID = 1
};
ActivitiesMock.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived))).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
}
(我知道有不同的編寫方式,這是我們嘗試過的最新迭代。)
ActivitiesMock
與Get(long ID, Recruiter User, bool IsArchived = false)
的collection
Get(long ID, Recruiter User, bool IsArchived = false)
。 我們最近編寫了包裝器,以便更有效地嘗試測試我們的實體調用,但是在嘗試驗證調用是否正確時,我們遇到了這個錯誤:
測試方法ExampleProject.Tests.Backend.Repositories.ActivityRepositoryTests.GetDoesThings拋出異常:Moq.MockException:模擬上的預期調用至少一次,但從未執行過:x => x.FirstOrDefault(y =>(y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID)&&(!(y.Archived)|| .IsArchived))
配置設置:x => x.FirstOrDefault(y =>(y.ID == .ID && y.Recruiter.CompanyID == .User.CompanyID)&&(!(y.Archived)|| .IsArchived)),Times 。決不
執行調用:IAppCollection`2.FirstOrDefault(x =>(((x.ID == value(ExampleProject.Backend.Repositories.ActivityRepository + <> c__DisplayClass2_0).ID)AndAlso(x.Recruiter.CompanyID == value(ExampleProject.Backend) .Repositories.ActivityRepository + <> c__DisplayClass2_0).User.CompanyID))AndAlso(Not(x.Archived)OrElse值(ExampleProject.Backend.Repositories.ActivityRepository + <> c__DisplayClass2_0).IsArchived)))
在這個例子中,包裝器( collection
)是接口的模擬。 目標是確保存儲庫在包裝器上調用正確的表達式,以便我們知道傳遞給Entity DbSet的謂詞是正確的,而不必擔心所有混亂的異步抽象。
運行測試時似乎找不到帶有完整謂詞的Mock Setup()
,當我將Setup()
更改為It.IsAny<Expression<Func<Activity, bool>>>()
它運行mock並提供返回,但Verify
調用不起作用。 所以,運行:
ActivitiesMock.Setup(x => x.FirstOrDefault(It.IsAny<Expression<Func<Activity, bool>>>())).ReturnsAsync(new Activity());
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)));
傳遞斷言但是驗證失敗,而運行:
ActivitiesMock
.Setup(x => x.FirstOrDefault(y => y.ID == ID && y.Recruiter.CompanyID == User.CompanyID && (!y.Archived || IsArchived)))
.ReturnsAsync(new Activity())
.Verifiable();
Activity result = await repo.Get(ID, User);
Assert.IsNotNull(result);
ActivitiesMock.Verify();
斷言失敗。
看起來它失敗了,因為它期望相同的對象類型。 我試圖做一些Moq無法處理的事情,或者我錯過了一些我需要做的事情才能使驗證正確無誤?
根據請求,LINQ-to-Entity包裝器( collection
)的具體實現是:
public Task<T> FirstOrDefault(Expression<Func<T, bool>> Predicate)
{
return DbSet.FirstOrDefaultAsync(Predicate);
}
雖然包裝器本身沒有被使用,但它的界面正在被嘲笑,而且我們正在測試它的模擬。
答案都不是。 由於匿名函數必須創建類實例來存儲所提供的數據,因此它們創建DisplayClass
實例來保存數據。 由於這些實例是在不同的名稱空間(以及其他內容)中創建的,因此當Moq調用.Equals
,它們不會傳遞。
我們通過編寫測試來解決這個問題:
ActivitiesMock
.Setup(x => x.Where(It.IsAny<Expression<Func<Activity, bool>>>()))
.Returns((Expression<Func<Activity, bool>> x) =>
{
actualPredicate = x;
return queryMock.Object;
});
然后創建有效和無效的活動以提供給謂詞,以確保它正確返回true
或false
:
Assert.IsTrue(actualPredicate.Compile().Invoke(validActivity));
雖然現在有點笨拙,但乍一看它似乎不像垃圾箱火災解決方案,它是我們確保所提供的電話按照我們期望的方式進行的一種方式,這就是我們想要的。
更新(2016年9月7日):到目前為止,這對我們來說一直很好。 我們遇到了LINQ-to-Entity語句沒有按預期運行的問題,因為LINQ區分大小寫且生成的SQL不是,但由於這不是我們的交易破壞者,我們只是精細。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.