ChatGPT解决这个技术问题 Extra ChatGPT

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

当我查看 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 ) 有什么大的优势。

如果结构变得更复杂,那么消息可能会很好,但您是否看到更多优势?可读性?


C
Clint Eastwood

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

但是,当您进行更复杂的检查时,优势就会变得更加明显:

val foo = List.of("someValue");
assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));
Expected: is <true>
         but: was <false>

对比

val foo = List.of("someValue");
assertThat(foo, containsInAnyOrder("someValue", "anotherValue"));
Expected: iterable with items ["someValue", "anotherValue"] in any order
     but: no item matches: "anotherValue" in ["someValue"]

可以讨论其中哪一个更容易阅读,但是一旦断言失败,您会从 assertThat 中得到一个很好的错误消息,但来自 assertTrue 的信息量非常少。


我的脑海里也有这个问题。谢谢,我从来没有这样想过。
它还有助于每个测试一个断言的“规则”,并更容易与 BDD 样式规范混合。
并且它将断言机制与条件(这是导致更好的错误消息的原因)分开。
该示例令人难以置信,因为几乎没有人会使用单个 assertTrue&&。即使在 JUnit 中,将其分成两个条件也会使问题原因显而易见。不要误会我的意思;我同意你的观点,我只是不喜欢你的例子。
M
Manur

4.4 版(引入它的地方)的 JUnit release notes 说明了四个优点:

更具可读性和可输入性:此语法允许您根据主语、动词、宾语(断言“x 为 3”)而不是 assertEquals 来思考,后者使用动词、宾语、主语(断言“等于 3 x”)组合:任何匹配器语句 s 可以被否定(not(s))、组合(either(s).or(t))、映射到集合(each(s))或用于自定义组合(afterFiveSeconds(s)) 可读的失败消息. (...) 自定义匹配器。通过自己实现 Matcher 接口,您可以为自己的自定义断言获得上述所有好处。

来自创建新语法的人的更详细的论证:here


I
Igor Popov

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

除了 hamcrest,您还可以使用 fest assertions。与 hamcrest 相比,它们具有 一些优势,例如:

它们更具可读性 (assertEquals(123, actual); // 读取“断言等于 123 是实际的” vs assertThat(actual).isEqualTo(123); // 读取“断言实际等于 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));

2016 年 10 月 17 日更新

Fest 不再活动,请改用 AssertJ


Fest 似乎已经死了,但 fork AssertJ 非常活跃。
A
Andy Davis

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

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

assertEqual(1, foo);

- 或者 -

assertThat(foo, is(1));

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

使用第二个版本,几乎不可能犯这个错误。


...并且当 Eclipse 报告断言失败时,如果您在传统的 assertThat() 中错误地放置参数,则该错误没有意义。
M
MartinL

例子:

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!

你可以让你的测试更具体你得到一个更详细的异常,如果测试失败更容易阅读测试

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


更好的是,我会在 assertThat 情况下省略字符串参数,因为您自动获得的消息同样提供信息:“预期:(大于 <1> 和小于 <3> 的值) "
是的你是对的。我编辑我的答案。最初我想同时使用(Matcher).and(Matcher) 但它没有用。
u
user4515828
assertThat(frodo.getName()).isEqualTo("Frodo");

接近自然语言。

更容易阅读,更容易分析代码。程序员花更多的时间来分析代码而不是编写新代码。因此,如果代码易于分析,那么开发人员应该更有效率。

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


好的,还有……?我建议通过解释为什么这是一件好事来支持你的论点。
s
samshers

assertThat 与 assertEquals 相比有优势 - 1)更具可读性 2)有关失败的更多信息 3)编译时错误 - 而不是运行时错误 4)编写测试条件的灵活性 5)可移植 - 如果您使用的是 hamcrest - 您可以使用 jUnit或 TestNG 作为底层框架。