Table of Contents
ToggleGetting Started with AssertJ
如何開始撰寫斷言,驗證單元測試程式碼,首先需要將物件傳遞給 Assertions.assertThat 方法,然後再執行實際的斷言,支援了一系列類別和實用方法,像是 Standard Java 、 Java 8 、 Guava 及靜態方法 allOf 、 byLessThan 、 contentOf 、 entry 、 within 等輔助方法,透過 AssertJ Getting Started 流式斷言,可以讓開發者體驗更流暢的驗證斷言,更方便快速撰寫單元測試,本篇增加了範例,透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- spring_boot_168
| +- test
| +- assertj
| +- GettingStartedAssertJTest.java
單元測試
Improve Assertions Using Assertj 斷言物件的主要目的是取得物件以進行斷言。
Fruit
建立 Fruit 類別,覆寫 toString ,定義屬性和方法,用來建立一個物件。
@NoArgsConstructor
@Getter
@Setter
public static class Fruit {
private String name;
private double quantity;
private int type;
private List<String> origins = new ArrayList<>();
public Fruit(String name, double quantity, int type) {
this.name = name;
this.quantity = quantity;
this.type = type;
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
builder.appendSuper(super.toString());
builder.append("name", name);
builder.append("quantity", quantity);
builder.append("type", type);
builder.append("origins", origins);
return builder.toString();
}
}
Object Assertions
驗證物件是否符合條件,若不成立,則會拋出 AssertionError ,當 isEqualTo 比較物件引用時會測試失敗,如果想比較它們的內容,要使用 usingRecursiveComparison ,遞迴比較屬性是否相等。
@Test
public void objectAssertions() {
Fruit apple = new Fruit("Apple", Double.MAX_VALUE, 1, Arrays.asList("Australia"));
Fruit apple2 = new Fruit("Apple", Double.MAX_VALUE, 1, Arrays.asList("Australia"));
System.out.println(apple);
System.out.println(apple2);
assertThatCode(() -> {
assertThat(apple).isEqualTo(apple2);
}).isInstanceOf(AssertionError.class);
// @Deprecated
assertThat(apple).isEqualToComparingFieldByFieldRecursively(apple2);
// use
assertThat(apple).usingRecursiveComparison().isEqualTo(apple2);
}
{"name":"Apple","quantity":1.7976931348623157E308,"type":1,"origins":[Australia]}
{"name":"Apple","quantity":1.7976931348623157E308,"type":1,"origins":[Australia]}
Class Assertions
Improve Assertions Using Assertj 驗證類別是否符合條件,若不成立,則會拋出 AssertionError 。
@Test
public void classAssertions() {
Class<?> clazz = List.class;
System.out.println(clazz);
assertThat(clazz).isInterface().isPublic();
Class<?> clazz2 = Collection.class;
System.out.println(clazz2);
assertThat(clazz2).isAssignableFrom(clazz).hasNoSuperclass().hasPublicMethods("add");
}
interface java.util.List
interface java.util.Collection
File Assertions
Improve Assertions Using Assertj 驗證檔案是否符合條件,若不成立,則會拋出 AssertionError 。
@Test
public void fileAssertions() {
File file = new File("./build.gradle");
System.out.println(file);
assertThat(file).exists().isFile().isRelative();
assertThat(file).canRead().canWrite();
File file2 = new File("./README.md");
System.out.println(file2);
assertThat(contentOf(file2)).startsWith("# Ruoxue").contains("www.ruoxue.org");
}
.\build.gradle
.\README.md
InputStream Assertions
Improve Assertions Using Assertj 驗證 InputStream 是否符合條件,若不成立,則會拋出 AssertionError 。
@Test
public void inputStreamAssertions() {
byte[] value = "AssertJ".getBytes();
System.out.println(Arrays.toString(value));
InputStream inputStream = new ByteArrayInputStream(value);
assertThat(inputStream).isNotEmpty();
byte[] intValue = BigInteger.valueOf(155).toByteArray();
System.out.println(Arrays.toString(intValue));
InputStream inputStream2 = new ByteArrayInputStream(intValue);
assertThat(inputStream2).isNotNull();
}
[65, 115, 115, 101, 114, 116, 74]
[0, -101]
Throwable Assertions
Guide to Assertj 驗證拋出例外,若不成立,則會拋出 AssertionError 。
@Test
public void throwableAssertions() {
List<String> list = Arrays.asList("AssertJ", "155");
assertThatThrownBy(() -> {
list.get(2);
}).isInstanceOf(IndexOutOfBoundsException.class);
assertThatCode(() -> list.get(2)).isInstanceOf(IndexOutOfBoundsException.class);
}
[{"name":"Durian","quantity":1.7976931348623157E308,"type":2,"origins":[]}, {"name":"Guava","quantity":1.0,"type":2,"origins":[]}, {"name":"Pitaya","quantity":-1.0,"type":2,"origins":[]}]
[1, 2, 3, 4, 5]
Describing Assertions
Guide to Assertj 設定驗證描述,當驗證失敗時,顯示自定義訊息。
@Test
public void describingAssertions() {
Fruit banana = new Fruit("Banana", 1, 3);
try {
assertThat(banana.getType()).as("%s's type should be equal to 2", banana.getName()).isEqualTo(2);
} catch (AssertionError e) {
e.printStackTrace();
assertThat(e).hasMessageContaining("type");
}
String[] array = new String[] { "Durian", "Guava", "Pitaya" };
String text = "Length expected: [" + 3 + "] but was: [" + array.length + "]";
try {
Supplier<String> desc = () -> text;
assertThat(array).as(desc).hasSize(2);
} catch (AssertionError e) {
e.printStackTrace();
assertThat(e).hasMessageContaining(text);
}
}
org.opentest4j.AssertionFailedError: [Banana's type should be equal to 2]
expected: 2
but was: 3
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at org.ruoxue.spring_boot_168.test.assertj.GettingStartedAssertJTest.describingAssertions(GettingStartedAssertJTest.java:125)
java.lang.AssertionError: [Length expected: [3] but was: [3]]
Expected size: 2 but was: 3 in:
["Durian", "Guava", "Pitaya"]
at org.ruoxue.spring_boot_168.test.assertj.GettingStartedAssertJTest.describingAssertions(GettingStartedAssertJTest.java:135)
GettingStartedAssertJTest.java
Guide to Assertj 新增單元測試,驗證是否符合預期。
package org.ruoxue.spring_boot_168.test.assertj;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.contentOf;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.jupiter.api.Test;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class GettingStartedAssertJTest {
@NoArgsConstructor
@Getter
@Setter
@Builder
public static class Fruit {
private String name;
private double quantity;
private int type;
private List<String> origins = new ArrayList<>();
public Fruit(String name, double quantity, int type, List<String> origins) {
this.name = name;
this.quantity = quantity;
this.type = type;
this.origins = origins;
}
public Fruit(String name, double quantity, int type) {
this(name, quantity, type, new ArrayList<>());
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
builder.appendSuper(super.toString());
builder.append("name", name);
builder.append("quantity", quantity);
builder.append("type", type);
builder.append("origins", origins);
return builder.toString();
}
}
@Test
public void objectAssertions() {
Fruit apple = new Fruit("Apple", Double.MAX_VALUE, 1, Arrays.asList("Australia"));
Fruit apple2 = new Fruit("Apple", Double.MAX_VALUE, 1, Arrays.asList("Australia"));
System.out.println(apple);
System.out.println(apple2);
assertThatCode(() -> {
assertThat(apple).isEqualTo(apple2);
}).isInstanceOf(AssertionError.class);
// @Deprecated
assertThat(apple).isEqualToComparingFieldByFieldRecursively(apple2);
// use
assertThat(apple).usingRecursiveComparison().isEqualTo(apple2);
}
@Test
public void classAssertions() {
Class<?> clazz = List.class;
System.out.println(clazz);
assertThat(clazz).isInterface().isPublic();
Class<?> clazz2 = Collection.class;
System.out.println(clazz2);
assertThat(clazz2).isAssignableFrom(clazz).hasNoSuperclass().hasPublicMethods("add");
}
@Test
public void fileAssertions() {
File file = new File("./build.gradle");
System.out.println(file);
assertThat(file).exists().isFile().isRelative();
assertThat(file).canRead().canWrite();
File file2 = new File("./README.md");
System.out.println(file2);
assertThat(contentOf(file2)).startsWith("# Ruoxue").contains("www.ruoxue.org");
}
@Test
public void inputStreamAssertions() {
byte[] value = "AssertJ".getBytes();
System.out.println(Arrays.toString(value));
InputStream inputStream = new ByteArrayInputStream(value);
assertThat(inputStream).isNotEmpty();
byte[] intValue = BigInteger.valueOf(155).toByteArray();
System.out.println(Arrays.toString(intValue));
InputStream inputStream2 = new ByteArrayInputStream(intValue);
assertThat(inputStream2).isNotNull();
}
@Test
public void throwableAssertions() {
List<String> list = Arrays.asList("AssertJ", "155");
assertThatThrownBy(() -> {
list.get(2);
}).isInstanceOf(IndexOutOfBoundsException.class);
assertThatCode(() -> list.get(2)).isInstanceOf(IndexOutOfBoundsException.class);
}
@Test
public void describingAssertions() {
Fruit banana = new Fruit("Banana", 1, 3);
try {
assertThat(banana.getType()).as("%s's type should be equal to 2", banana.getName()).isEqualTo(2);
} catch (AssertionError e) {
e.printStackTrace();
assertThat(e).hasMessageContaining("type");
}
String[] array = new String[] { "Durian", "Guava", "Pitaya" };
String text = "Length expected: [" + 3 + "] but was: [" + array.length + "]";
try {
Supplier<String> desc = () -> text;
assertThat(array).as(desc).hasSize(2);
} catch (AssertionError e) {
e.printStackTrace();
assertThat(e).hasMessageContaining(text);
}
}
}
心得分享
Assertj Guide 支援物件、類別、檔案、例外等進行斷言,驗證是否符合條件,並可以設定驗證描述,當驗證失敗時,顯示自定義訊息,善用 Guide to Assertj 將提升不少驗證效率,縮短撰寫單元測試的時間。
原碼下載