根据《单元测试的艺术》一书中的观点,一个良好的单元测试包括三个步骤:
- 准备测试环境和数据;
- 执行目标方法;
- 验证执行结果(判断程序的运行结果是否如你所想)。
我们一般使用断言(Assert)进行结果验证,Junit的org.junit.Assert包提供了大量断言API,如:assertEquals、assertTrue和assertNotNull等等。总体来说,Junit的断言API还可以,功能不算强大,只能说是满足需求。
接下来介绍下本文的主角:AssertJ。AssertJ的slogan是:Fluent assertions for java。如果读者了解构建链模式或者Java 8的stream&filter,那么就可以体会到这种思路的好处了。
接下来看看官网给出的例子(确实比Junit强大很多):
// unique entry point to get access to all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;
// in the following examples, fellowshipOfTheRing is a List of [TolkienCharacter](https://github.com/joel-costigliola/assertj-examples/blob/java-8/assertions-examples/src/main/java/org/assertj/examples/data/TolkienCharacter.java)
// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron)
.isIn(fellowshipOfTheRing);
// String specific assertions
assertThat(frodo.getName()).startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo");
// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
// Java 8 exception assertion
assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
// Java 8 BDD style exception
assertion Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
assertThat(thrown).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
// using extracting magical feature to check fellowshipOfTheRing characters name :)
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// Extracting with Java 8 love (type safe)
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// Extracting multiple values at once (using tuple to group them)
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));
// filter collection before
assertionassertThat(fellowshipOfTheRing).filteredOn("race", HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
// filter collection with Java 8
PredicateassertThat(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 : map, dates (java 7 and java 8), file, numbers, optional ...