使用 AssertJ 進行軟斷言
一、簡介
在本教程中,我們將研究 AssertJ 的軟斷言功能,回顧其動機,並討論其他測試框架中的類似解決方案。
2. 動機
首先,我們應該要理解為什麼存在軟斷言。為此,讓我們探討以下範例:
@Test
void test_HardAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
Assertions.assertThat(result.getId()).isNull();
Assertions.assertThat(result.getType()).isEqualTo(1);
Assertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
}
這段程式碼非常簡單。我們有一個映射器,它將請求實體映射到某個DomainModel
實例。我們想測試這個映射器的行為。當然,我們認為只有在所有斷言都通過的情況下映射器才能正常工作。
現在,假設我們的映射器有缺陷,並且錯誤地映射了id和status
。在這種情況下,如果我們啟動此測試,我們將收到AssertionFailedError
錯誤:
org.opentest4j.AssertionFailedError:
expected: null
but was: "73a3f292-8131-4aa9-8d55-f0dba77adfdb"
這一切都很好,除了一件事——雖然id
的映射確實是錯誤的,但status
的映射也是錯誤的。測試並沒有告訴我們status
映射不正確,它只是抱怨id
。發生這種情況是因為,預設情況下,當我們將斷言與assertj
一起使用時,我們將它們用作硬斷言。這意味著測試中第一個未通過的斷言將立即觸發AssertionError
。
乍一看,這似乎沒什麼大不了的,因為我們只需要啟動兩次測試——第一次發現我們的 ID 映射是錯誤的,最後發現我們的狀態映射是錯誤的。然而,如果我們的映射器更複雜,並且它映射具有數十個字段的實體(這在現實項目中是可能的),那麼我們將花費大量時間通過不斷重新運行測試來查找所有問題。具體來說,為了解決這種不便, assertj
為我們提供了軟斷言。
3.AssertJ中的軟斷言
軟斷言透過做一件非常簡單的事情來解決這個問題 - 收集所有斷言期間遇到的所有錯誤並產生單一報告。讓我們透過重寫上面的測試來看看軟斷言如何幫助我們進行assertj
:
@Test
void test_softAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions.assertSoftly(softAssertions -> {
softAssertions.assertThat(result.getId()).isNull();
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
});
}
在這裡,我們要求assertj
輕柔地執行一系列斷言。這意味著無論如何,上面 lambda 表達式中的所有斷言都會執行,並且如果其中一些斷言生成錯誤,這些錯誤將被打包到單一報告中,如下所示:
org.assertj.core.error.AssertJMultipleFailuresError:
Multiple Failures (3 failures)
-- failure 1 --
expected: null
but was: "66f8625c-b5e4-4705-9a49-94db3b347f72"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:19)
-- failure 2 --
expected: 1
but was: 0
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:20)
-- failure 3 --
expected: "DRAFT"
but was: "NEW"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:21)
它在調試過程中非常有用,可以縮短捕獲所有錯誤所花費的時間。此外,值得一提的是,還有另一種方法可以在assertj
中使用軟斷言來編寫測試:
@Test
void test_softAssertionsViaInstanceCreation() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.getId()).isNull();
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
softAssertions.assertAll();
}
在本例中,我們只是直接建立SoftAssertions
的實例,而不是在前面的範例中,在前面的範例中仍然會建立SoftAssertions
實例,但在框架罩下。
關於這兩個變體之間的差異,從功能的角度來看,它們是完全相同的。所以我們可以隨意選擇我們想要的任何方法。
4.其他測試框架中的軟斷言
由於軟斷言作為一項功能非常有用,因此許多測試框架已經採用了它們。棘手的是,在不同的框架中,該功能可能有不同的名稱,甚至可能根本沒有名稱。例如,我們在 Junit5 中有一個assertAll()
,它的工作方式類似,但有自己的風格。 TestNG 還具有軟斷言作為一項功能,這與assertj
中的軟斷言非常相似。
因此,關鍵在於大多數著名的測試框架都具有軟斷言作為功能,儘管它們可能沒有這個名稱。
5. 總結
最後,讓我們在一個表中總結一下硬斷言和軟斷言之間的所有差異和相似之處:
硬性斷言 | 軟斷言 | |
---|---|---|
是預設的斷言模式 | 真的 | 錯誤的 |
大多數框架都支援(包括assertj ) |
真的 | 真的 |
表現出快速失敗行為 | 真的 | 錯誤的 |
六,結論
在本文中,我們探討了軟斷言及其動機。與硬斷言相反,即使某些斷言失敗,軟斷言也允許我們繼續測試執行,在這種情況下,框架會產生詳細的報告。這使得軟斷言成為一個有用的功能,因為它們使調試變得更容易和更快。
最後,軟斷言並不是 Assertj 的獨特功能。它們也存在於其他流行的框架中,可能具有不同的名稱或沒有名稱。
與往常一樣,本文的程式碼可在 GitHub 上取得。