你能为AssertJ断言添加自定义消息吗?

时间:2015-03-11 18:14:26

标签: java unit-testing junit assertj

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

我们经常比较非人类可读性的对象,并且会有随机看似的ID或UUID,并且不可能通过它们包含的数据来判断它们应该是什么。遗憾的是,对于我们的代码库来说,这是一个不可避免的情况,因为它实现的目的之一是在其他服务之间映射数据而不必理解它是什么。

在JUnit中,assertThat方法在String reason参数之前提供了Matcher<T>参数的版本。这样就可以添加一个简短的调试字符串来解决问题,例如比较对人类意味着什么。

另一方面,AssertJ提供了大量不同的genericized static assertThat方法,它们返回某种形式的interface Assert或其众多实现类之一。此接口不提供设置要包含在故障中的自定义消息的标准方法。

有没有办法从AssertJ API或其扩展程序中获取此功能而无需create a custom assert class for every assert type我们想要添加消息?

5 个答案:

答案 0 :(得分:103)

以经典的方式,我在发布问题后找到了我想要的东西。希望这将使下一个人更容易找到,而不必首先知道它的名称。神奇的方法是看似简短的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中的引用字符串将出现在单元测试输出日志中。


我通过注意问题中链接的custom assert page中的failWithMessage帮助程序找到了此信息。该方法的JavaDoc指出它受到保护,因此调用者无法使用它来设置自定义消息。但它确实提到了as助手:

  

此外,此方法遵循用as(String, Object...)设置的任何描述或用overridingErrorMessage(String, Object...)用户定义的覆盖错误消息。

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

在功能高亮页面之前,AssertJ主页没有提及任何助手,该页面显示了Soft Assertions部分中as助手的示例,但没有直接描述它的作用。

答案 1 :(得分:2)

仅供参考,这在新的AssertJ网站(仍在建设中,但已经有有用的信息)中进行了说明,请参见https://assertj.github.io/doc/#assertj-core-assertion-description

答案 2 :(得分:2)

为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中对此进行了说明。

答案 3 :(得分:2)

目前提到的两个选项是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,输出变成:

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.

答案 4 :(得分:0)

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

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