您可以向 AssertJ assertThat 添加自定义消息吗?
Can you add a custom message to AssertJ assertThat?
我们有一个测试套件,主要使用 JUnit 断言和 Hamcrest 匹配器。我们的一个团队开始试验 AssertJ 并以其语法、灵活性和声明性给人们留下了深刻印象。 JUnit 提供的一项功能我在 AssertJ 中找不到与之对应的功能:添加自定义断言失败消息。
我们经常比较的对象不是为人类可读性而设计的,它们具有看似随机的 ID 或 UUID,并且不可能通过它们包含的数据来判断它们应该是什么。遗憾的是,对于我们的代码库来说,这是不可避免的情况,因为它实现的部分目的是在其他服务之间映射数据,而不必了解它是什么。
在 JUnit 中,assertThat
方法在 Matcher<T>
参数之前提供一个带有 String reason
参数的版本。这使得添加一个简短的调试字符串来阐明问题变得微不足道,比如比较对人类意味着什么。
另一方面,AssertJ 提供了数以亿计的不同 genericized static assertThat
methods which return some form of interface Assert 或其众多实现之一 类。此接口不提供设置要包含在故障中的自定义消息的标准方法。
有没有什么方法可以从 AssertJ API 或其扩展之一获得此功能,而不必 create a custom assert class for every assert type 我们要向其中添加消息?
以经典的方式,我在发布问题后立即找到了我要找的东西。希望这将使下一个人更容易找到,而不必先知道它的名字。神奇的方法是具有欺骗性的短名称 as
, which is part of another interface that AbstractAssert
implements: Descriptable,而不是基本的 Assert 接口。
public S as(String description, Object... args)
Sets the description of this object supporting String.format(String, Object...)
syntax.
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]>");
}
catch 块中引用的字符串 hasMessage
是断言失败时出现在单元测试输出日志中的内容。
我通过注意到 custom assert page linked in the question. The JavaDoc 中的 failWithMessage
帮助程序发现该方法指出它是受保护的,因此调用者不能使用它来设置自定义消息。但是它确实提到了 as
助手:
Moreover, this method honors any description set with as(String, Object...)
or overridden error message defined by the user with overridingErrorMessage(String, Object...)
.
... 和 overridingErrorMessage 助手,它用提供的新字符串完全替换了标准的 AssertJ expected: ... but was:...
消息。
AssertJ 主页在功能突出显示页面之前没有提到任何一个帮助程序,该页面在 Soft Assertions 部分显示了 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
的不同之处在于,它让您 完全控制自定义消息 - 没有 "expected" 和 "but was" .
这在测试的实际值对演示无用的情况下很有用 - 此方法允许您显示其他可能计算的值,或者根本 none。
请注意,就像 Descriptable.as
一样,您必须在 任何实际断言之前调用 withFailMessage()
- 否则它将不起作用,因为断言将先开火。这在 Javadoc 中有说明。
使用 AssertJ 中的内置 as()
方法。例如:
assertThat(myTest).as("The test microservice is not active").isEqualTo("active");
目前提到的两个选项是as
和withFailMessage
,语法和用法我就不再赘述了。要了解它们之间的区别以及它们各自的用处,请考虑我们正在测试导出指标的用例:
// 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.
如果没有 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.
我们有一个测试套件,主要使用 JUnit 断言和 Hamcrest 匹配器。我们的一个团队开始试验 AssertJ 并以其语法、灵活性和声明性给人们留下了深刻印象。 JUnit 提供的一项功能我在 AssertJ 中找不到与之对应的功能:添加自定义断言失败消息。
我们经常比较的对象不是为人类可读性而设计的,它们具有看似随机的 ID 或 UUID,并且不可能通过它们包含的数据来判断它们应该是什么。遗憾的是,对于我们的代码库来说,这是不可避免的情况,因为它实现的部分目的是在其他服务之间映射数据,而不必了解它是什么。
在 JUnit 中,assertThat
方法在 Matcher<T>
参数之前提供一个带有 String reason
参数的版本。这使得添加一个简短的调试字符串来阐明问题变得微不足道,比如比较对人类意味着什么。
AssertJ 提供了数以亿计的不同 genericized static assertThat
methods which return some form of interface Assert 或其众多实现之一 类。此接口不提供设置要包含在故障中的自定义消息的标准方法。
有没有什么方法可以从 AssertJ API 或其扩展之一获得此功能,而不必 create a custom assert class for every assert type 我们要向其中添加消息?
以经典的方式,我在发布问题后立即找到了我要找的东西。希望这将使下一个人更容易找到,而不必先知道它的名字。神奇的方法是具有欺骗性的短名称 as
, which is part of another interface that AbstractAssert
implements: Descriptable,而不是基本的 Assert 接口。
public S as(String description, Object... args)
Sets the description of this object supporting
String.format(String, Object...)
syntax.
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]>"); }
catch 块中引用的字符串 hasMessage
是断言失败时出现在单元测试输出日志中的内容。
我通过注意到 custom assert page linked in the question. The JavaDoc 中的 failWithMessage
帮助程序发现该方法指出它是受保护的,因此调用者不能使用它来设置自定义消息。但是它确实提到了 as
助手:
Moreover, this method honors any description set with
as(String, Object...)
or overridden error message defined by the user withoverridingErrorMessage(String, Object...)
.
... 和 overridingErrorMessage 助手,它用提供的新字符串完全替换了标准的 AssertJ expected: ... but was:...
消息。
AssertJ 主页在功能突出显示页面之前没有提到任何一个帮助程序,该页面在 Soft Assertions 部分显示了 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
的不同之处在于,它让您 完全控制自定义消息 - 没有 "expected" 和 "but was" .
这在测试的实际值对演示无用的情况下很有用 - 此方法允许您显示其他可能计算的值,或者根本 none。
请注意,就像 Descriptable.as
一样,您必须在 任何实际断言之前调用 withFailMessage()
- 否则它将不起作用,因为断言将先开火。这在 Javadoc 中有说明。
使用 AssertJ 中的内置 as()
方法。例如:
assertThat(myTest).as("The test microservice is not active").isEqualTo("active");
目前提到的两个选项是as
和withFailMessage
,语法和用法我就不再赘述了。要了解它们之间的区别以及它们各自的用处,请考虑我们正在测试导出指标的用例:
// 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.
如果没有 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.