是否可以将自定义消息添加到 AssertJ assertThat?

2022-08-31 08:30:52

我们有一个测试套件,主要将 JUnit 断言与 Hamcrest 匹配器结合使用。我们的团队之一开始尝试使用AssertJ,并以其语法,灵活性和声明性给人留下了深刻的印象。JUnit提供了一个功能,我在 AssertJ 中找不到等效的功能:添加自定义断言失败消息。

我们经常比较那些不是为人类可读性而制作的对象,这些对象将具有随机的ID或UUID,并且不可能通过它们包含的数据来判断它们应该是什么。对于我们的代码库来说,这是一个不可避免的情况,可悲的是,它实现的目的的一部分是在其他服务之间映射数据,而不必了解它是什么。

在 JUnit 中,assertThat 方法提供一个版本,该版本在参数之前有一个参数。这使得添加一个简短的调试字符串来阐明问题变得微不足道,就像比较对人类意味着什么一样。String reasonMatcher<T>

另一方面,AssertJ提供了一个jillion不同的泛型化静态assert方法,这些方法返回某种形式的接口Assert或其众多实现类之一。此接口不提供将自定义消息设置为包含在失败中的标准方法。

有没有办法从 AssertJ API 或其扩展之一获取此功能,而不必为我们要向其添加消息的每个断言类型创建自定义断言类


答案 1

以经典的方式,我在发布问题后不久就找到了我想要的东西。希望这将使下一个人更容易找到,而不必首先知道它叫什么。magic 方法是看似短命名的 as,它是另一个实现的接口的一部分:可描述,而不是基本 Assert 接口。AbstractAssert

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


我通过在问题中链接的自定义断言页面中注意到帮助程序来发现这一点。该方法的JavaDoc指出它是受保护的,因此调用方不能使用它来设置自定义消息。但是,它确实提到了帮助者:failWithMessageas

此外,此方法支持用户使用 定义的任何描述集或被覆盖的错误消息。as(String, Object...)overridingErrorMessage(String, Object...)

...和 overoveringErrorMessage 帮助程序,它用提供的新字符串完全替换了标准的 AssertJ 消息。expected: ... but was:...

AssertJ 主页在功能突出显示页面之前不会提及任何一个帮助程序,该页面在“软断言”部分中显示了帮助程序的示例,但没有直接描述它的作用。as


答案 2

要在Patrick M的答案中添加另一个选项:

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

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

这在所测试的实际值对表示没有用处的情况下很有用 - 此方法允许您改为显示其他可能计算的值,或者根本不显示任何值。


请注意,就像 一样,您必须在任何实际断言之前调用 - 否则它将不起作用,因为断言将首先触发。这在 Javadoc 中有所说明。Descriptable.aswithFailMessage()


推荐