簡體   English   中英

這是一個糟糕的Moq設置還是Moq的缺陷?

[英]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)));
}

(我知道有不同的編寫方式,這是我們嘗試過的最新迭代。)

ActivitiesMockGet(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;
    });

然后創建有效和無效的活動以提供給謂詞,以確保它正確返回truefalse

Assert.IsTrue(actualPredicate.Compile().Invoke(validActivity));

雖然現在有點笨拙,但乍一看它似乎不像垃圾箱火災解決方案,它是我們確保所提供的電話按照我們期望的方式進行的一種方式,這就是我們想要的。

更新(2016年9月7日):到目前為止,這對我們來說一直很好。 我們遇到了LINQ-to-Entity語句沒有按預期運行的問題,因為LINQ區分大小寫且生成的SQL不是,但由於這不是我們的交易破壞者,我們只是精細。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM