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

150 java testing junit hamcrest junit4

当我查看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
Run Code Online (Sandbox Code Playgroud)

比方说,我没有看到一个很大的优势assertEquals( 0, 1 ).

如果构造变得更复杂但是你看到更多的优点,那么对于消息可能很好吗?可读性?

Joa*_*uer 170

assertFoo存在与您的意图完全匹配的存在的情况没有大的优势.在这些情况下,他们的行为几乎相同.

但是当你来检查更复杂的检查时,优势就变得更加明显:

assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));
Run Code Online (Sandbox Code Playgroud)

assertThat(foo, hasItems("someValue", "anotherValue"));
Run Code Online (Sandbox Code Playgroud)

可以讨论哪一个更易于阅读,但是一旦断言失败,您将从中获得良好的错误消息assertThat,但只能获得非常少量的信息assertTrue.

assertThat会告诉你断言是什么以及你得到了什么.assertTrue只会告诉你,你到达了false预期的位置true.

  • 我心里也有这个疑问。谢谢,我从来没有以这种方式想过。 (2认同)
  • 它将断言机制与条件(导致更好的错误消息)分开. (2认同)
  • 这个例子难以置信,因为几乎没有人会使用`&&`来使用单个`assertTrue`.将它分成两个条件会使问题在JUnit中显而易见.别误会我的意思; 我同意你的观点,我只是不喜欢你的榜样. (2认同)

Man*_*nur 46

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

  • 更具可读性和可输入性:这种语法允许您根据主语,动词,对象(断言"x是3")而不是assertEquals来思考,它使用动词,宾语,主语(断言"等于3 x")
  • 组合:任何匹配语句s时,可以否定(否(S) ),组合(或者(一个或多个).或者(t)的),映射到集合(每个(S) ),或在自定义组合使用(afterFiveSeconds(S))
  • 可读的失败消息.(......)
  • 自定义匹配器.通过自己实现Matcher接口,您可以获得自己的自定义断言的所有上述好处.

来自创建新语法的人的更详细的论证:这里.


Igo*_*pov 39

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

除了hamcrest,你也可以使用fest断言.它们比hamcrest一些优点,例如:

  • 它们更具可读性
    (assertEquals(123, actual); // reads "assert equals 123 is actual"vs
    assertThat(actual).isEqualTo(123); // reads "assert that actual is equal to 123")
  • 它们是可发现的(您可以使用任何IDE进行自动完成).

一些例子

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));
Run Code Online (Sandbox Code Playgroud)

2016年10月17日更新

Fest不再活跃,而是使用AssertJ.

  • 巨星似乎已经死了,但是叉子AssertJ非常活跃. (4认同)

And*_*vis 18

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

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

assertEqual(1, foo);
Run Code Online (Sandbox Code Playgroud)

- 要么 -

assertThat(foo, is(1));
Run Code Online (Sandbox Code Playgroud)

使用第一种方法,很容易忘记正确的顺序,并向后键入它.然后,而不是说测试失败,因为它预期1并得到2,消息是倒退的.测试通过时不成问题,但在测试失败时可能导致混淆.

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


Mar*_*inL 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!
Run Code Online (Sandbox Code Playgroud)
  1. 你可以让你的测试更加特别
  2. 如果测试失败,你会得到更详细的异常
  3. 更容易阅读测试

顺便说一下:你也可以在assertXXX中写文字......