为什么我应该使用Hamcrest-Matcher和assertThat()而不是传统的assertXXX()-方法


153

当我查看Assert类JavaDoc中的示例时

assertThat("Help! Integers don't work", 0, is(1)); // fails:
// failure message:
// Help! Integers don't work
// expected: is <1> 
// got value: <0>
assertThat("Zero is one", 0, is(not(1))) // passes

可以说,我看不出有什么大的优势assertEquals( 0, 1 )

如果结构变得更复杂,但是对于消息,这很好,但是您看到更多的优势了吗?可读性?

Answers:


172

在这种情况下,没有太大优势 assertFoo完全符合您的意图的。在这些情况下,它们的行为几乎相同。

但是,当您检查比较复杂的支票时,其优势将变得更加明显:

assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));

assertThat(foo, hasItems("someValue", "anotherValue"));

可以讨论其中的哪一个更容易阅读,但是一旦断言失败,您将收到来自的良好错误消息assertThat,而来自的信息却很少assertTrue

assertThat会告诉您断言是什么以及您得到了什么。assertTrue只会告诉你你达到了false预期true


我也有这个问题。谢谢,我从未想过这种方式。
小麦味

1
它还有助于实现每个测试一个声明的“规则”,并更轻松地与BDD风格的规范融合。
尼尔斯·沃卡

2
并且它将断言机制与条件分开(这是导致更好的错误消息的原因)。
2010年

2
的例子是不可信作为几乎没有人会使用一个单一的assertTrue&&。将其分为两个条件使该问题甚至在JUnit中也很明显。不要误会我的意思;我同意你的观点,只是不喜欢你的榜样。
maaartinus

48

版本4.4(在其引入的地方)的JUnit 发行说明指出了四个优点:

  • 更具可读性和可键入性:此语法使您可以根据主语,动词,宾语(断言“ x为3”)而不是使用使用动词,宾语,主语(断言“等于3 x”)的assertEquals
  • 组合:任何匹配语句s时,可以否定(否(S) ),组合(或者(一个或多个)。或者(t)的),映射到集合(每个(S) ),或在自定义组合使用(afterFiveSeconds(S)
  • 可读的故障消息。(...)
  • 自定义匹配器。通过自己实现Matcher接口,您可以为自己的自定义断言获得上述所有好处。

创建新语法的人提供了更详细的论点:此处


39

基本上是为了增加代码的可读性

除了hamcrest之外,您还可以使用fest断言与hamcrest相比,它们具有一些优点,例如:

一些例子

import static org.fest.assertions.api.Assertions.*;

// common assertions
assertThat(yoda).isInstanceOf(Jedi.class);
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);
assertThat(frodo).isIn(fellowshipOfTheRing);
assertThat(sauron).isNotIn(fellowshipOfTheRing);

// String specific assertions
assertThat(frodo.getName()).startsWith("Fro").endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .excludes(sauron);


// map specific assertions (One ring and elves ring bearers initialized before)
assertThat(ringBearers).hasSize(4)
                       .includes(entry(Ring.oneRing, frodo), entry(Ring.nenya, galadriel))
                       .excludes(entry(Ring.oneRing, aragorn));

2016年10月17日更新

Fest不再处于活动状态,请改用AssertJ


4
Fest似乎已经死了,但是叉子AssertJ仍然活着。
Amedee Van Gasse

18

一个非常基本的理由是很难弄乱新语法。

假设测试后特定值foo应该为1。

assertEqual(1, foo);

- 要么 -

assertThat(foo, is(1));

使用第一种方法,很容易忘记正确的顺序,然后向后键入。然后,不是说测试由于期望1并得到2而失败,而是向后发送消息。测试通过时不是问题,但测试失败时可能导致混乱。

对于第二个版本,几乎不可能犯此错误。


...并且当Eclipse报告断言失败时,如果在传统的assertThat()中以错误的方式放置参数,该错误就没有意义。
Sridhar Sarnobat '16

9

例:

assertThat(5 , allOf(greaterThan(1),lessThan(3)));
//  java.lang.AssertionError:
//  Expected: (a value greater than <1> and a value less than <3>)
//       got: <5>
assertTrue("Number not between 1 and 3!", 1 < 5 && 5 < 3);
//  java.lang.AssertionError: Number not between 1 and 3!
  1. 您可以使测试更加具体
  2. 如果测试失败,则会得到更详细的异常
  3. 更容易阅读测试

顺便说一句:您也可以在assertXXX中编写Text ...


1
更好的是,在这种assertThat情况下,我将省略字符串参数,因为您自动收到的消息也具有丰富的信息:“预期:(值大于<1>且小于<3>)”
MatrixFrog

是的,你是对的。我编辑我的答案。本来我想同时使用(Matcher)。和(Matcher),但没有用。
MartinL 2011年

3
assertThat(frodo.getName()).isEqualTo("Frodo");

接近自然语言。

易于阅读,易于分析代码。与编写新代码相比,程序员花费更多的时间分析代码。因此,如果代码易于分析,则开发人员应提高工作效率。

PS Code应该是写得很好的书。自我记录的代码。


4
好吧,……?我建议通过解释为什么这是一件好事来支持您的论点。
内森·塔吉

0

与assertEquals相比,使用assert断言有很多优点
-1)可读性强
2)有关故障的更多信息
3)编译时错误-而非运行时错误
4)编写测试条件时具有灵活性
5)可移植-如果您使用hamcrest-您可以使用jUnit或以TestNG作为基础框架。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.