简体   繁体   English

您可以向 AssertJ assertThat 添加自定义消息吗?

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

We have a test suite that primarily uses JUnit assertions with Hamcrest matchers.我们有一个测试套件,主要使用带有 Hamcrest 匹配器的 JUnit 断言。 One of our team started experimenting with AssertJ and impressed people with its syntax, flexibility and declarative-ness.我们的一个团队开始试验AssertJ ,它的语法、灵活性和声明性给人们留下了深刻的印象。 There is one feature that JUnit provides that I can't find an equivalent for in AssertJ: adding a custom assert failure message. JUnit 提供了一项我在 AssertJ 中找不到的等效功能:添加自定义断言失败消息。

We're often comparing objects that are not made for human readability and will have random-seeming Ids or UUIDs and it's impossible to tell what they're supposed to be by the data they contain.我们经常比较那些不是为了人类可读性而制作的对象,它们具有随机的 Id 或 UUID,并且不可能通过它们包含的数据来判断它们应该是什么。 This is an unavoidable situation for our codebase, sadly, as part of the purpose it fulfills is mapping data between other services without necessarily understanding what it is.遗憾的是,对于我们的代码库来说,这是不可避免的情况,因为它实现的部分目的是在其他服务之间映射数据,而不必了解它是什么。

In JUnit, the assertThat method provides a version with a String reason parameter before the Matcher<T> param.在 JUnit 中, assertThat方法在Matcher<T>参数之前提供了一个带有String reason参数的版本。 This makes it trivial to add a short debug string shedding some light on the problem, like what the comparison should mean to a human.这使得添加一个简短的调试字符串来阐明问题变得微不足道,比如比较对人类意味着什么。

AssertJ, on the other hand, provides a jillion different genericized static assertThat methods which return some form of interface Assert or one of its many implementing classes.另一方面,AssertJ 提供了无数不同的泛化static assertThat方法,这些方法返回某种形式的接口 Assert或其许多实现类中的一个。 This interface does not provide a standard way of setting a custom message to be included with failures.此接口不提供设置要包含在故障中的自定义消息的标准方法。

Is there any way to get this functionality from the AssertJ API or one of its extensions without having to create a custom assert class for every assert type we want to add messages to?有什么方法可以从 AssertJ API 或其扩展之一获得此功能,而不必为我们想要添加消息的每个断言类型创建自定义断言类

And in classic fashion, I found what I was looking for moments after posting the question.以经典的方式,我在发布问题后找到了我正在寻找的东西。 Hopefully this will make it easier for the next person to find without first having to know what it's called.希望这会让下一个人更容易找到,而不必先知道它叫什么。 The magic method is the deceptively short-named as , which is part of another interface that AbstractAssert implements: Descriptable , not the base Assert interface.神奇的方法是具有欺骗性的短名称as ,它是AbstractAssert实现的另一个接口的一部分: Descriptable ,而不是基本的 Assert 接口。

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

Sets the description of this object supporting String.format(String, Object...) syntax.设置此对象的描述支持String.format(String, Object...)语法。
Example :例子 :

 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]>"); }

Where that quoted string in the catch block hasMessage is what appears in your unit test output log if the assertion fails.如果断言失败,catch 块hasMessage中引用的字符串是单元测试输出日志中出现的内容。


I found this by noticing the failWithMessage helper in the custom assert page linked in the question.我通过注意到问题中链接的自定义断言页面中failWithMessage帮助器发现了这一点。 The JavaDoc for that method points out that it is protected, so it can't be used by callers to set a custom message.该方法的JavaDoc指出它是受保护的,因此调用者不能使用它来设置自定义消息。 It does however mention the as helper:然而,它确实提到了as助手:

Moreover, this method honors any description set with as(String, Object...) or overridden error message defined by the user with overridingErrorMessage(String, Object...) .此外,此方法遵循使用as(String, Object...)设置的任何描述或用户使用overridingErrorMessage(String, Object...)定义的覆盖错误消息。

... and the overridingErrorMessage helper, which completely replaces the standard AssertJ expected: ... but was:... message with the new string provided. ......而overridingErrorMessage帮手,它完全取代了标准的AssertJ expected: ... but was:...所提供的新的字符串消息。

The AssertJ homepage doesn't mention either helper until the features highlights page, which shows examples of the as helper in the Soft Assertions section, but doesn't directly describe what it does.在功能突出显示页面之前,AssertJ 主页没有提到任何一个助手,该页面在软断言部分显示了as助手的示例,但没有直接描述它的作用。

To add another option to Patrick M's answer:为 Patrick M 的答案添加另一个选项:

Instead of using Descriptable.as , you can also use AbstractAssert.withFailMessage() :除了使用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");
}

The difference to using Descriptable.as is that it gives you complete control over the custom message - there is no "expected" and "but was".使用Descriptable.as的不同之处在于它让您可以完全控制自定义消息- 没有“预期”和“但是”。

This is useful where the actual values being tested are not useful for presentation - this method allows you to show other, possibly calculated values instead, or none at all.当被测试的实际值对演示没有用时,这很有用 - 此方法允许您显示其他可能计算的值,或者根本不显示。


Do note that, just like Descriptable.as , you must call withFailMessage() before any actual assertions - otherwise it will not work, as the assertion will fire first.请注意,就像Descriptable.as一样,您必须任何实际断言之前调用withFailMessage() - 否则它将不起作用,因为断言将首先触发。 This is noted in the Javadoc.这在 Javadoc 中有说明。

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

Use the inbuilt as() method in AssertJ.使用 AssertJ 中内置的as()方法。 For example:例如:

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

The two options mentioned so far are as and withFailMessage , so I won't go into the syntax or usage again.到目前为止提到的两个选项是aswithFailMessage ,所以我不会再次讨论语法或用法。 To see the difference between them, and how each can be useful, consider the use case where we are testing metrics being exported:要了解它们之间的区别,以及它们的用途,请考虑我们正在测试导出的指标的用例:

// 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)
}

I've used Java 11 syntax to reduce some boilerplate.我使用 Java 11 语法来减少一些样板文件。

Without the withFailMessage , if the meter isn't present in the map, the default output contains a dump of all entries in the map, which clutters the test log.如果没有withFailMessage ,如果地图中不存在仪表,则默认输出包含地图中所有条目的转储,这会使测试日志withFailMessage We don't care what other meters are present, only that the one we want is there.我们不关心其他仪表是否存在,只关心我们想要的仪表在那里。

Using withFailMessage , the output becomes:使用withFailMessage ,输出变为:

java.lang.AssertionError: Meter blah is not found

As for as , it only appends the given message to the output, but retains the failed comparison, which is very useful.至于as ,它只将给定的消息附加到输出中,但保留了失败的比较,这非常有用。 We get:我们得到:

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