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

rn0zuynd  于 2022-11-11  发布在  其他
关注(0)|答案(4)|浏览(133)

我们有一个测试套件,主要使用JUnitAssert和Hamcrest匹配器。我们的一个团队开始试验AssertJ,它的语法、灵活性和声明性给人们留下了深刻的印象。JUnit提供的一个特性,我在AssertJ中找不到它的等价物:添加自定义Assert失败消息。
我们经常比较的对象不是为了人类的可读性而设计的,它们的ID或UUID看起来随机,而且不可能通过它们所包含的数据来判断它们应该是什么。这对我们的代码库来说是不可避免的情况,遗憾的是,它所实现的部分目的是在其他服务之间Map数据,而不必理解它是什么。
在JUnit中,assertThat方法提供了一个在Matcher<T>参数之前有一个String reason参数的版本,这使得添加一个简短的调试字符串来阐明问题变得很容易,比如比较对人类意味着什么。
另一方面,AssertJ提供了无数不同的泛型化static assertThat方法,这些方法返回某种形式的interface Assert或它的许多实现类中的一个。
有没有办法从AssertJ API或它的扩展之一获得此功能,而不必向create a custom assert class for every assert type添加消息?

piztneat

piztneat1#

按照传统的方式,我在发布问题后很快就找到了我想要的东西。希望这能让下一个人更容易找到它,而不必先知道它的名字。神奇的方法是一个看起来很短的名称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主页直到特性突出显示页面才提到这两个helper,该页面在软Assert部分显示了as helper的示例,但没有直接描述它的功能。

djp7away

djp7away2#

  • 在帕特里克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一样,您必须在任何实际Assert * 之前 * 调用withFailMessage()-否则它将不起作用,因为Assert将首先触发。这在Javadoc中有说明。

cotxawn7

cotxawn73#

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

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

4uqofj5v4#

到目前为止提到的两个选项是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)
}

我已经使用Java11语法来减少一些样板文件。
如果没有withFailMessage,如果map中不存在计量器,默认输出包含map中所有条目的转储,这会使测试日志变得混乱。我们不关心其他计量器是否存在,只关心我们想要的计量器是否存在。
使用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.

相关问题