繁体   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