AssertJ

起因

我们在使用JUnit测试的时候,一般情况下会使用它的Assert类下的各种assertXXX方法。JUnit包含了一个依赖Hamcrest,这是一个断言库。我们可以使用断言库来进行复杂的断言操作。

但是Hamcrest有几个缺点:

  • 缺乏更新,我看了看MavenCentral上,Hamcrest库最近更新还是2019年,因此对新的Java 新版本支持可能不够充分;

  • Matcher分散在多个类中,编写困难,我们为了编写类似assertThat(resultString, anyOf(containsString("name"), containsString("lastname")));这样的代码,需要静态引入多个类;

  • JUnit仅依赖了Hamcrest核心包,只附带了最基本的断言功能,如果我们希望断言数字大小之类的话,还需要自己引入Hamcrest完整包,比较麻烦。

基于以上原因,出现了AssertJ库 AssertJ - fluent assertions java library

使用

引包

AssertJ有1、2、3三个版本,分别用于JDK6、7、8(及以上)。要在JDK 8中使用AssertJ,在项目中添加如下依赖

<dependency>
  	<groupId>org.assertj</groupId>
  	<artifactId>assertj-core</artifactId>
  	<!-- use 2.9.1 for Java 7 projects -->
  	<version>3.11.1</version>
  	<scope>test</scope>
</dependency>

示例

在测试类中静态导入所有断言
import static org.assertj.core.api.Assertions.*;
...
//由于AssertJ的断言操作全在一个类中,所以当我们输入.之后,IDE就会嗖嗖的把所有断言列出来。
assertThat(objectUnderTest).//断言操作

// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);

// chaining string specific assertions
assertThat(frodo.getName()).startsWith("Fro")
                           .endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions (there are plenty more)
// in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .doesNotContain(sauron);

// as() is used to describe the test and will be shown before the error message
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);

// exception assertion, standard style ...
assertThatThrownBy(() -> { throw new Exception("boom!"); }).hasMessage("boom!");
// ... or BDD style
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
assertThat(thrown).hasMessageContaining("boom");

// using the 'extracting' feature to check fellowshipOfTheRing character's names
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
                               .doesNotContain("Sauron", "Elrond");

// extracting multiple values at once grouped in tuples
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
                               .contains(tuple("Boromir", 37, "Man"),
                                         tuple("Sam", 38, "Hobbit"),
                                         tuple("Legolas", 1000, "Elf"));

// filtering a collection before asserting
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir);

// combining filtering and extraction (yes we can)
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir)
                               .extracting(character -> character.getRace().getName())
                               .contains("Hobbit", "Elf", "Man");

// and many more assertions: iterable, stream, array, map, dates, path, file, numbers, predicate, optional ...

扩展

AssertJ支持所有Java基本类型,所以我们只要引入这一个包就可以满足大多数需求。如果需要JodaTime等支持,引入相应AssertJ扩展即可。