Hamcrest vs. AssertJ
在RESTful API测试期间,通常需要调用REST端点并验证其响应以检查其功能,这并不罕见。 但是,如何验证某项工作是否正常? 答案是断言。 断言是完整测试过程的基石,无论是RESTful API还是其他组件。 当我们说"测试断言"时,我们的意思是检查布尔表达式是否为真,除非测试用例存在问题或错误。
在RESTful API测试中,您可能会处理API特定的断言,例如响应代码或长度验证。 此外,在很多情况下,即使您仅使用API,也需要每天执行常规声明。 这些可以是变量验证,日期比较等。
在本文中,我们将讨论两种最流行的Java断言框架,它们可以免费使用,它们可以在您的任何项目中使用。 它们可以用作RESTful API测试的一部分,也可以用于测试中需要执行的任何其他检查。 这些框架的名称为AssertJ和Hamcrest,我们将比较它们的开放源代码,语法,类结构和功能。
Hamcrest是最著名的Java框架之一,它有助于以Java编程语言编写测试。 有关框架名称的一个有趣事实:它是作为"匹配器"一词的变位词而创建的,"匹配器"在逻辑上等于"断言"。 由于该框架的高度流行,后来将其移植到其他编程语言,例如C ++,C#,Objective-C,Python,ActionScript 3,PHP,JavaScript,Erlang和R。
AssertJ不像Hamcrest那样知名,但是与此同时,它的流行在过去几年中增长很快。 与从默认的Java测试框架JUnit继承的Hamcrest的经典断言语法相反,AssertJ的主要思想是提供流畅的语法。 其主要目的是提高代码的可读性。 值得一提的是,AssertJ是FEST Assert项目的一个分支,这是AssertJ创建的第一步。
现在让我们开始进行比较。
开源社区参与和普遍欢迎
当您选择一个周围有活跃社区的框架时,这总是一个好兆头。 活跃社区的主要标志是框架存储库中的活动,这基本上意味着正在进行的开发和维护。 如果框架是开源的,则始终可以找到其代码存储库,并验证其拥有新代码的频率。
joel-costigliola / assertj
如您所见,AssertJ在过去几年中拥有更稳定的开发模式,并且仍然每月提交一次。
Humcrest / Jawmakrest

现在让我们看一下这些框架的普遍流行性。 让我们看看过去一年Google趋势向我们展示了什么。


如您所见,Hamcrest无疑拥有更广泛的受众。 但是,另一方面,在过去的12个月中,AssertJ的受欢迎程度似乎有所增长(尽管很小)。 另一方面,Hamcrest保持在同一水平上。
您可能想知道为什么Hamcrest在这一点上越来越受欢迎。 这可能是因为Hamcrest的生命周期更长。 Hamcrest的第一个版本最早于2007年5月在Maven存储库中发布,而AssertJ的第一个版本仅在2013年3月才添加到Maven存储库中。因此比较过去5年的趋势可能是公平的:


现在您可以看到差异的原因。 当开发AssertJ的第一个版本时,除了常用的测试框架提供的默认工具之外,Hamcrest已经拥有大量的人,并且是主要的断言库。 但是,当已经有实现相同目标的可用选项列表时,为什么有人又想创建一个框架呢? 这正是我们要调查的内容。
句法
虽然这两种工具都能很好地完成工作,但是语法上的差异却非常明显。 让我们基于一些基本的断言示例来研究两个框架的主要语法:
简单断言
1 2 | assertThat(a, equalTo(b)); //Hamcrest assertThat(a).isEqualTo(b); //AssertJ |
日期断言
1 2 | assertThat(tomorrow, isAfter(today)); //Hamcrest assertThat(tomorrow).isAfter(today); //AssertJ |
列出断言
1 2 3 4 5 6 7 | assertThat(list, Matchers.<Collection<String>> allOf(CoreMatchers.hasItem("a"), CoreMatchers.not(CoreMatchers.hasItem("b")) )); //Hamcrest assertThat(list). contains("a"). doesNotContain("b"); //AssertJ |
空断言
1 2 | assertThat(a, nullValue()); //Hamcrest assertThat(actual).isNull(); //AssertJ |
带有自定义消息的断言
1 2 | assertThat("Error", a, equalTo(b)); //Hamcrest assertThat(a).isEqualTo(b).overridingErrorMessage("Error"); //AssertJ |
由您决定最喜欢哪种语法,但是对我而言,AssertJ感觉更自然且易于阅读。
断言类结构
除了语法差异外,这些框架还具有不同的类层次结构。 虽然AssertJ仅提供一个包含所有可用断言的静态类,但是Hamcrest具有不同的类。 如果您的文件包含许多不同类型的断言,则所有类都将充满这样的导入:
1 2 3 4 5 6 | import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.empty; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; |
维护所有这些导入无需花费太多精力,因为所有主要开发环境都会自动处理导入。 但是,当您的代码更干净时,它仍然很好,很简单,例如AssertJ:
1 | import static org.assertj.core.api.Assertions.*; |
由于相同的原因,AssertJ提供了更方便的自动建议,因为您无需记住和猜测要用于查找所需断言的类匹配:

软断言功能
虽然这两个框架都提供了执行经典断言的出色功能,但是只有AssertJ为您提供了额外的功能。 这种"东西"是软断言的概念。
软断言允许您执行多次检查并查看所有检查的结果。 对于单元测试,有一个很好的模式可以为每个单元测试保留一个断言。 但是不要忘记,同一断言库可用于不同的集成测试中,在这些集成测试中,多个断言是常见的做法。 可以通过以下方式使用AssertJ软断言:
1 2 3 4 5 6 7 8 9 10 11 | @Test public void soft_assertion_assertj_test(){ User user = new User("Yuri","Bushnev","[email protected]"); SoftAssertions softly = new SoftAssertions(); softly.assertThat(user).isNotNull(); softly.assertThat(user.getName()).isEqualTo("Yuri"); softly.assertThat(user.getSurename()).isEqualTo(""); softly.assertThat(user.getEmail()).isEqualTo("bushnevyurigmail.com"); softly.assertAll(); } |
如您所见,它并不太复杂。 在上面的示例中,我们期望在验证姓氏和电子邮件期间出现两个断言错误。 一种标准方式将需要三个断言一个接一个。 然后,我们只会看到第一个关于该姓氏的失败声明,而电子邮件中的问题将被隐藏,直到该姓氏被修复为止。 当对复杂的应用程序进行回归测试时,此类问题通常在现实生活中可重现-当在第一次测试运行中迅速捕获到一些错误时,但是许多其他错误可能会被遗漏并仅在以后发现,因为直到失败的步骤被确定之前,测试无法继续进行 固定。 但是使用软断言,您会看到类似以下内容:
1 2 3 4 5 6 7 | org.assertj.core.api.SoftAssertionError: The following 2 assertions failed: 1) expected:<"[]"> but was:<"[Bushnev]"> at AssertJTests.soft_assertion_assertj_test(AssertJTests.java:16) expected:<"[]"> but was:<"[Bushnev]"> 2) expected:<"bushnevyuri[]gmail.com"> but was:<"bushnevyuri[@]gmail.com"> at AssertJTests.soft_assertion_assertj_test(AssertJTests.java:17) expected:<"bushnevyuri[]gmail.com"> but was:<"bushnevyuri[@]gmail.com"> |
AssertJ提供的另一个有趣的功能是迁移器工具,它可以帮助您从JMeter或其他框架迁移到AssertJ。 如果您决定迁移所有断言,那么这绝对是一个有用的功能。 但是请记住,从一个断言库迁移到另一个断言库并没有关键的需要。 花点时间,逐步进行,并确保在重构过程中不产生任何回归。
结论
本文的目的不是要敦促您使用确切的库或强迫进行测试的某种方式。 相反,其目的是向您展示最流行的Java断言框架,如果您想扩展标准断言的功能。
最重要的是,您选择自己喜欢的工具。 请记住,编写好的断言时,分析技能比工具更有用。 话虽如此,花几个小时不时尝试一些新的东西非常有用。 从我的角度来看,AssertJ绝对值得一看(如果您还没有这样做的话)。
如果您想了解更多信息,可以查看更多的断言库。 每种编程语言和每种RESTful API验证工具都有其自己的断言库,可帮助用户轻松实现各种断言。
这些默认库和内置库是一个很好的起点,因为它们有大量的文档也涵盖了它们的历史。 自从这些库已经使用了很多年以来,这是最稳定的方法,而且很早以前就已经发现并修复了问题和错误。 但是,一旦您获得了更多的经验,您可能会感到除了默认框架之外,还有其他可能。 开始挖掘之后,您可以找到满足各种需求的大量可用解决方案。