Hamcrest vs.AssertJ

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创建的第一步。

现在让我们开始进行比较。

开源社区参与和普遍欢迎

当您选择一个周围有活跃社区的框架时,这总是一个好兆头。 活跃社区的主要标志是框架存储库中的活动,这基本上意味着正在进行的开发和维护。 如果框架是开源的,则始终可以找到其代码存储库,并验证其拥有新代码的频率。


Humcrest / Jawmakrest

Image title

joel-costigliola / assertj Image title

如您所见,AssertJ在过去几年中拥有更稳定的开发模式,并且仍然每月提交一次。

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

Image title

Image title

如您所见,Hamcrest无疑拥有更广泛的受众。 但是,另一方面,在过去的12个月中,AssertJ的受欢迎程度似乎有所增长(尽管很小)。 另一方面,Hamcrest保持在同一水平上。

您可能想知道为什么Hamcrest在这一点上越来越受欢迎。 这可能是因为Hamcrest的生命周期更长。 Hamcrest的第一个版本最早于2007年5月在Maven存储库中发布,而AssertJ的第一个版本仅在2013年3月才添加到Maven存储库中。因此比较过去5年的趋势可能是公平的:

Image title

Image title

现在您可以看到差异的原因。 当开发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提供了更方便的自动建议,因为您无需记住和猜测要用于查找所需断言的类匹配:

Image title

软断言功能

虽然这两个框架都提供了执行经典断言的出色功能,但是只有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验证工具都有其自己的断言库,可帮助用户轻松实现各种断言。

这些默认库和内置库是一个很好的起点,因为它们有大量的文档也涵盖了它们的历史。 自从这些库已经使用了很多年以来,这是最稳定的方法,而且很早以前就已经发现并修复了问题和错误。 但是,一旦您获得了更多的经验,您可能会感到除了默认框架之外,还有其他可能。 开始挖掘之后,您可以找到满足各种需求的大量可用解决方案。