Skip to content

AssertJ

Calvin Xiao edited this page Jan 18, 2014 · 17 revisions

Overview

JUnit自己的Assert.assertEquals(String message, String expected, String actual) 是公认的烂API,你很难记住三个参数的位置,所以后来有了Hamcrest改成一个从左到右的读法,assertThat(actual, equalTo(expected)); 但仍然不是很让人满意,所以后来又有了小三Fest Assertion上位,但它后来又不怎么更新了,最后fork了一个AssertJ 出来,最新的代码是这样写的,

import static org.assertj.core.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)
                               .doesNotContain(sauron);
// throwable specific assertions
try {
  fellowshipOfTheRing.get(9); 
  failBecauseExceptionWasNotThrown(IndexOutOfBoundsException.class);
} catch (Exception e) {
  assertThat(e).isInstanceOf(IndexOutOfBoundsException.class)
               .hasMessage("Index: 9, Size: 9")
               .hasNoCause();
}

// map specific assertions (One ring and elves ring bearers initialized before)
assertThat(ringBearers).hasSize(4)
                       .contains(entry(Ring.oneRing, frodo), entry(Ring.nenya, galadriel))
                       .doesNotContain(entry(Ring.oneRing, aragorn));

可见,新的语法,可以更顺畅的从左往右读,可以连续的测试,而且对各种常见对象都做了封装,包括Class,基本对象,基本类型, Collections, Map, Date, Exception, File,还有扩展模块支持Guva里的collection和Joda-time。

在Eclipse里的配置

要从Junit的assertEquals()方便的换成assertThat(),重要一点是Eclipse能自动提示你import static org.assertj.core.api.Assertions.*; 在Eclipse 点击 preferences > Java > Editor > Content assist > Favorites > New Type,加入org.assertj.core.api.Assertions,IntelliJ的方法类似。

另外,假设你已经配置了Eclipse,static import总是给* 而不是展开。Java->Code Style->Organize Import, 设置Number of static imports needed of * to 1.

没在上例里演示的技巧

  • 加入错误描述,比如assertThat(myList).as("list should be empty").isEmpty(),as()要写在测试的前面。
  • 下一个版本,assertThat()可以简写成BDD风格的then(),更短。
  • extracting函数,用字符串定义需要反射取出的属性/方法,比如遍历一个List,将里面每个对象的属性拿出来判断。
assertThat(userList).extracting("name").contains("calvin", "kevin");
  • Date的比较可以忽略时间上一些毫秒级的不同,只要是同一秒,或者相差在一秒内的就算对。
assertThat(date1).isEqualToIgnoringSeconds(date2);
assertThat(date1).isInSameSecondWindowAs(date2);
  • assertThat(oneLineXml).isXmlEqualTo(expectedXml); 忽略XML字符串之间格式化之间的差别,只比较实际内容。
  • 两个Bean之间逐个比较某些特定的fields而不是用equals()函数比较,isEqualToIgnoringGivenFields,isEqualToComparingOnlyGivenFields,isEqualToIgnoringNullFields 等。
  • File有hasContent(String expected),InputStream有hasContentEqualTo(InputStream expected)
  • 不在assert错误时立刻抛出异常,而是所有assert跑完后一次过抛出,免得fix完一个问题,再执行测试又才看到另一个。
  SoftAssertions softly = new SoftAssertions();
  softly.assertThat(mansion.guests()).as("Living Guests");
  softly.assertThat(mansion.kitchen()).as("Kitchen");
  softly.assertAll();