簡體   English   中英

您可以向 AssertJ assertThat 添加自定義消息嗎?

[英]Can you add a custom message to AssertJ assertThat?

我們有一個測試套件,主要使用帶有 Hamcrest 匹配器的 JUnit 斷言。 我們的一個團隊開始試驗AssertJ ,它的語法、靈活性和聲明性給人們留下了深刻的印象。 JUnit 提供了一項我在 AssertJ 中找不到的等效功能:添加自定義斷言失敗消息。

我們經常比較那些不是為了人類可讀性而制作的對象,它們具有隨機的 Id 或 UUID,並且不可能通過它們包含的數據來判斷它們應該是什么。 遺憾的是,對於我們的代碼庫來說,這是不可避免的情況,因為它實現的部分目的是在其他服務之間映射數據,而不必了解它是什么。

在 JUnit 中, assertThat方法在Matcher<T>參數之前提供了一個帶有String reason參數的版本。 這使得添加一個簡短的調試字符串來闡明問題變得微不足道,比如比較對人類意味着什么。

另一方面,AssertJ 提供了無數不同的泛化static assertThat方法,這些方法返回某種形式的接口 Assert或其許多實現類中的一個。 此接口不提供設置要包含在故障中的自定義消息的標准方法。

有什么方法可以從 AssertJ API 或其擴展之一獲得此功能,而不必為我們想要添加消息的每個斷言類型創建自定義斷言類

以經典的方式,我在發布問題后找到了我正在尋找的東西。 希望這會讓下一個人更容易找到,而不必先知道它叫什么。 神奇的方法是具有欺騙性的短名稱as ,它是AbstractAssert實現的另一個接口的一部分: Descriptable ,而不是基本的 Assert 接口。

public S as(String description, Object... args)

設置此對象的描述支持String.format(String, Object...)語法。
例子 :

 try { // set a bad age to Mr Frodo which is really 33 years old. frodo.setAge(50); // you can specify a test description with as() method or describedAs(), it supports String format args assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33); } catch (AssertionError e) { assertThat(e).hasMessage("[check Frodo's age] expected:<[33]> but was:<[50]>"); }

如果斷言失敗,catch 塊hasMessage中引用的字符串是單元測試輸出日志中出現的內容。


我通過注意到問題中鏈接的自定義斷言頁面中failWithMessage幫助器發現了這一點。 該方法的JavaDoc指出它是受保護的,因此調用者不能使用它來設置自定義消息。 然而,它確實提到了as助手:

此外,此方法遵循使用as(String, Object...)設置的任何描述或用戶使用overridingErrorMessage(String, Object...)定義的覆蓋錯誤消息。

......而overridingErrorMessage幫手,它完全取代了標准的AssertJ expected: ... but was:...所提供的新的字符串消息。

在功能突出顯示頁面之前,AssertJ 主頁沒有提到任何一個助手,該頁面在軟斷言部分顯示了as助手的示例,但沒有直接描述它的作用。

為 Patrick M 的答案添加另一個選項:

除了使用Descriptable.as ,您還可以使用AbstractAssert.withFailMessage()

try {
  // set a bad age to Mr Frodo which is really 33 years old.
  frodo.setAge(50);
  // you can specify a test description via withFailMessage(), supports String format args
  assertThat(frodo.getAge()).
    withFailMessage("Frodo's age is wrong: %s years, difference %s years",
      frodo.getAge(), frodo.getAge()-33).
    isEqualTo(33);
} catch (AssertionError e) {
  assertThat(e).hasMessage("Frodo's age is wrong: 50 years, difference 17 years");
}

使用Descriptable.as的不同之處在於它讓您可以完全控制自定義消息- 沒有“預期”和“但是”。

當被測試的實際值對演示沒有用時,這很有用 - 此方法允許您顯示其他可能計算的值,或者根本不顯示。


請注意,就像Descriptable.as一樣,您必須任何實際斷言之前調用withFailMessage() - 否則它將不起作用,因為斷言將首先觸發。 這在 Javadoc 中有說明。

僅供參考,新的AssertJ網站(目前仍在建設中,但已經有了有用的信息),請參閱https://assertj.github.io/doc/#assertj-core-assertion-description

使用 AssertJ 中內置的as()方法。 例如:

 assertThat(myTest).as("The test microservice is not active").isEqualTo("active");

到目前為止提到的兩個選項是aswithFailMessage ,所以我不會再次討論語法或用法。 要了解它們之間的區別,以及它們的用途,請考慮我們正在測試導出的指標的用例:

// map of all metrics, keyed by metrics name
Map<String, Double> invocations = ...

List.of(
    "grpc.client.requests.sent",
    "grpc.client.responses.received",
    "grpc.server.requests.received",
    "grpc.server.responses.sent"
).forEach { counter ->
    var meter = // create meter name using counter
    assertThat(invocations)
        .withFailMessage("Meter %s is not found", meter)
        .containsKey(meter)
    assertThat(invocations.get(meter))
        .as(meter)
        .isEqualTo(0.0)
}

我使用 Java 11 語法來減少一些樣板文件。

如果沒有withFailMessage ,如果地圖中不存在儀表,則默認輸出包含地圖中所有條目的轉儲,這會使測試日志withFailMessage 我們不關心其他儀表是否存在,只關心我們想要的儀表在那里。

使用withFailMessage ,輸出變為:

java.lang.AssertionError: Meter blah is not found

至於as ,它只將給定的消息附加到輸出中,但保留了失敗的比較,這非常有用。 我們得到:

org.opentest4j.AssertionFailedError: [blah] 
Expecting:
 <1.0>
to be equal to:
 <0.0>
but was not.

暫無
暫無

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

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