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

Java 为什么我应该使用Hamcrest Matcher和assertThat()而不是传统的assertXXX()方法,java,testing,junit,junit4,hamcrest,Java,Testing,Junit,Junit4,Hamcrest,当我看到断言类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 断言(“帮助!整数不工作”,0是(

当我看到断言类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
断言(“帮助!整数不工作”,0是(1));//失败:
//失败消息:
//救命啊!整数不起作用
//预期:是吗
//获得价值:
assertThat(“零是一”,0,is(不是(1))//通过
我看不出与assertEquals(0,1)相比有什么大的优势


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

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

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

val foo = List.of("someValue");
assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));
我们可以讨论其中哪一个更容易阅读,但是一旦断言失败,您将从
assertThat
中得到一条很好的错误消息,但从
assertTrue
中只能得到非常少量的信息。JUnit for version 4.4(在引入它的地方)说明了四个优点:

  • 更具可读性和可键入性:该语法允许您根据主语、动词、宾语(断言“x为3”)而不是使用动词、宾语、主语(断言“等于3 x”)的assertEquals进行思考
  • 组合:任何匹配器语句都可以被求反(not)、组合(s).或(t))、映射到集合(每个集合)或用于自定义组合(五秒钟后)
  • 可读的故障消息。(……)
  • 定制匹配器。通过自己实现Matcher接口,您可以为自己的自定义断言获得上述所有好处
创建新语法的人提供了更详细的论证:。

示例:

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!
资产(5,所有(大于(1),小于(3));
//java.lang.AssertionError:
//预期值:(大于和小于的值)
//得到:
assertTrue(“数字不在1和3之间!”,1<5&&5<3);
//java.lang.AssertionError:数字不在1和3之间!
  • 您可以使您的测试更具体
  • 如果测试失败,您会得到一个更详细的异常
  • 更容易阅读测试

  • 顺便说一句:您也可以在assertXXX中编写文本…

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

    假设一个特定的值foo在测试后应该是1

    assertEqual(1, foo);
    
    --或--

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


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

    基本上是为了提高代码的可读性

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

    • 它们更具可读性
      assertEquals(123,实际值);//读取“assert等于123是实际值”
      vs
      isEqualTo(123);//读取“断言实际值等于123”)
    • 它们是可发现的()
    一些例子
    2016年10月17日更新 Fest不再处于活动状态,请改用。

    assertThat(frodo.getName()).isEqualTo(“frodo”);
    
    接近自然语言

    更容易阅读,更容易分析代码。 程序员花在分析代码上的时间比写新代码要多。所以,若代码易于分析,那个么开发人员应该更高效

    附言。 代码应该是一本写得很好的书。
    自文档化代码。

    与assertEquals相比,assertEquals有很多优点-
    1) 更具可读性
    2) 有关故障的更多信息
    3) 编译时错误-而不是运行时错误
    4) 书写测试条件的灵活性

    5) 可移植—如果您使用的是hamcrest—您可以使用jUnit或TestNG作为底层框架

    我心里也有这个问题。谢谢,我从来没有这样想过。它还有助于每个测试一个断言的“规则”,并且更容易与BDD风格的规范混合。它将断言机制与条件分离(这是导致更好的错误消息的原因)。这个例子令人难以置信,因为几乎没有人会将单个
    资产true
    &
    一起使用。将其分为两种情况,即使在JUnit中,问题也会很明显。别误会我;我同意你的观点,我只是不喜欢你的例子。更好的是,我会在
    assertThat
    案例中省略字符串参数,因为你自动得到的消息同样具有信息性:“预期:(值大于,值小于)”是的,你是对的。我编辑我的答案。最初我想同时使用(Matcher)和(Matcher),但都不起作用。好的,还有…?我建议通过解释为什么这是一件好事来支持您的论点。Fest似乎已经死了,但fork AssertJ非常活跃……当Eclipse报告断言失败时,如果您在传统的assertThat()中错误地放置了参数,那么错误就没有意义了。
    Expected: iterable with items ["someValue", "anotherValue"] in any order
         but: no item matches: "anotherValue" in ["someValue"]
    
    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!
    
    assertEqual(1, foo);
    
    assertThat(foo, is(1));
    
    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));