Android 單元測試Truth的使用

概述

2. Maven依賴

3.簡介

4.入門

5.標準的Java斷言

5.1。對象斷言

5.2。整數,浮點和雙斷言

5.3。大十進制斷言

5.4。布爾斷言

5.5。字符串斷言

5.6。數組斷言

5.7。可比斷言

5.8。迭代斷言

5.9。Map斷言

5.10。異常斷言

5.11。類斷言

6. Java 8斷言

6.1。可選斷言

6.2。流斷言

7.Guava斷言

7.1。可選斷言

7.2。多Map斷言

7.3。多集斷言

7.4。表斷言

8.自定義失敗消息和標籤

9.擴展

10.結論


1.概述

Truth是一種流暢,靈活的開源測試框架,旨在使測試斷言和失敗消息更具可讀性。

在本文中,我們將探索Truth框架的關鍵功能,並通過示例來展示其功能。

2. Maven依賴

首先,我們需要將添加TruthTruth-java8擴展到我們的pom.xml:

1

2

3

4

5

6

7

8

9

10

11

<dependency>

    <groupId>com.google.truth</groupId>

    <artifactId>truth</artifactId>

    <version>0.32</version>

</dependency>

<dependency>

    <groupId>com.google.truth.extensions</groupId>

    <artifactId>truth-java8-extension</artifactId>

    <version>0.32</version>

    <scope>test</scope>

</dependency>

 

 

或者項目工程下build.gradle 

dependencies {
    androidTestImplementation 'com.google.truth:truth:1.0'
}
   

 

您可以在Maven Central上找到Truth和Truth-java8-extension的最新版本。

3.簡介

Truth允許我們爲各種類編寫可讀的斷言和失敗消息:

  • 標準Java –基元,數組,字符串,對象,集合,可拋出對象,類等。

  • Java 8 – 可選和Stream實例

  • 番石榴 – 可選,Multimap,Multiset和Table對象

  • 自定義類型 –通過擴展Subject類,我們將在後面看到

通過Truth和Truth8類,該庫提供了實用的方法,可用於編寫對主題(即被測試的值或對象)起作用的斷言。

一旦知道了該主題,真相就可以在編譯時推斷該主題已知的命題。這使它可以返回圍繞我們的值的包裝器,這些包裝器聲明特定於該特定主題的命題方法。

例如,在對列表進行斷言時,Truth返回IterableSubject實例,該實例定義諸如contains()和containsAnyOf()等方法。在Map上聲明時,它返回一個MapSubject,該方法聲明諸如containsEntry()和containsKey()之類的方法。

4.入門

要開始編寫斷言,讓我們首先導入Truth的入口點:

1

2

import static com.google.common.truth.Truth.*;

import static com.google.common.truth.Truth8.*;

 

現在,讓我們編寫一個簡單的類,在下面的一些示例中將使用它們:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public class User {

    private String name = "John Doe";

    private List<String> emails

      = Arrays.asList("[email protected]", "[email protected]");

 

    public boolean equals(Object obj) {

        if (obj == null || getClass() != obj.getClass()) {

            return false;

        }

 

        User other = (User) obj;

        return Objects.equals(this.name, other.name);

    }

 

    // standard constructors, getters and setters

}

 

注意自定義的equals()方法,在該方法中,我們聲明兩個User對象(如果它們的名稱相同)是相等的。

5.標準的Java斷言

在本節中,我們將看到有關如何爲標準Java類型編寫測試斷言的詳細示例。

5.1。對象斷言

真相提供了主題包裝器,用於對對象執行斷言。Subject也是庫中所有其他包裝器的父級,並聲明用於確定Object(在我們的情況下爲User)是否等於另一個對象的方法:

1

2

3

4

5

6

7

@Test

public void whenComparingUsers_thenEqual() {

    User aUser = new User("John Doe");

    User anotherUser = new User("John Doe");

 

    assertThat(aUser).isEqualTo(anotherUser);

}

 

或等於列表中的給定對象:

1

2

3

4

5

6

@Test

public void whenComparingUser_thenInList() {

    User aUser = new User();

 

    assertThat(aUser).isIn(Arrays.asList(1, 3, aUser, null));

}

 

或如果不是:

1

2

3

4

5

6

@Test

public void whenComparingUser_thenNotInList() {

    // ...

 

    assertThat(aUser).isNotIn(Arrays.asList(1, 3, "Three"));

}

 

是否爲null:

1

2

3

4

5

6

7

8

9

10

11

12

13

@Test

public void whenComparingUser_thenIsNull() {

    User aUser = null;

 

    assertThat(aUser).isNull();

}

 

@Test

public void whenComparingUser_thenNotNull() {

    User aUser = new User();

 

    assertThat(aUser).isNotNull();

}

 

或者它是特定類的實例:

1

2

3

4

5

6

@Test

public void whenComparingUser_thenInstanceOf() {

    // ...

 

    assertThat(aUser).isInstanceOf(User.class);

}

 

Subject類中還有其他斷言方法。要發現所有內容,請參閱主題文檔

在以下各節中,我們將重點介紹 Truth支持的每種特定類型的最相關方法。但是,請記住,Subject類中的所有方法也可以應用。

5.2。整數,浮點和雙斷言

可以比較Integer,Float和Double實例的相等性:

1

2

3

4

5

6

@Test

public void whenComparingInteger_thenEqual() {

    int anInt = 10;

 

    assertThat(anInt).isEqualTo(10);

}

 

如果更大:

1

2

3

4

5

6

@Test

public void whenComparingFloat_thenIsBigger() {

    float aFloat = 10.0f;

 

    assertThat(aFloat).isGreaterThan(1.0f);

}

 

或更小:

1

2

3

4

5

6

@Test

public void whenComparingDouble_thenIsSmaller() {

    double aDouble = 10.0f;

 

    assertThat(aDouble).isLessThan(20.0);

}

 

此外,還可以檢查Float和Double實例以查看它們是否在預期的精度範圍內:

1

2

3

4

5

6

7

8

9

10

11

12

13

@Test

public void whenComparingDouble_thenWithinPrecision() {

    double aDouble = 22.18;

 

    assertThat(aDouble).isWithin(2).of(23d);

}

 

@Test

public void whenComparingFloat_thenNotWithinPrecision() {

    float aFloat = 23.04f;

 

    assertThat(aFloat).isNotWithin(1.3f).of(100f);

}

 

5.3。大十進制斷言

除了常見的斷言之外,還可以忽略這種類型的規模來進行比較:

1

2

3

4

5

6

@Test

public void whenComparingBigDecimal_thenEqualIgnoringScale() {

    BigDecimal aBigDecimal = BigDecimal.valueOf(1000, 3);

 

    assertThat(aBigDecimal).isEqualToIgnoringScale(new BigDecimal(1.0));

}

 

5.4。布爾斷言

僅提供了兩種相關方法,isTrue()和isFalse():

1

2

3

4

5

6

@Test

public void whenCheckingBoolean_thenTrue() {

    boolean aBoolean = true;

 

    assertThat(aBoolean).isTrue();

}

 

5.5。字符串斷言

我們可以測試字符串是否以特定文本開頭:

1

2

3

4

5

6

@Test

public void whenCheckingString_thenStartsWith() {

    String aString = "This is a string";

 

    assertThat(aString).startsWith("This");

}

 

另外,我們可以檢查字符串是否包含給定的String,是否以期望值結尾或是否爲空。這些和其他方法的測試用例在源代碼中可用。

5.6。數組斷言

我們可以檢查Array,看它們是否等於其他數組:

1

2

3

4

5

6

7

@Test

public void whenComparingArrays_thenEqual() {

    String[] firstArrayOfStrings = { "one", "two", "three" };

    String[] secondArrayOfStrings = { "one", "two", "three" };

 

    assertThat(firstArrayOfStrings).isEqualTo(secondArrayOfStrings);

}

 

或如果它們爲空:

1

2

3

4

5

6

@Test

public void whenCheckingArray_thenEmpty() {

    Object[] anArray = {};

 

    assertThat(anArray).isEmpty();

}

 

5.7。可比斷言

除了測試Comparable是否大於或小於另一個實例之外,我們還可以檢查它們是否至少爲給定值:

1

2

3

4

5

6

@Test

public void whenCheckingComparable_thenAtLeast() {

    Comparable<Integer> aComparable = 5;

 

    assertThat(aComparable).isAtLeast(1);

}

 

此外,我們可以測試它們是否在特定範圍內:

1

2

3

4

5

6

@Test

public void whenCheckingComparable_thenInRange() {

    // ...

 

    assertThat(aComparable).isIn(Range.closed(1, 10));

}

 

或在特定列表中:

1

2

3

4

5

6

@Test

public void whenCheckingComparable_thenInList() {

    // ...

 

    assertThat(aComparable).isIn(Arrays.asList(4, 5, 6));

}

 

我們還可以根據類的compareTo()方法測試兩個Comparable實例是否等效。

首先,讓我們修改User類以實現Comparable接口:

1

2

3

4

5

6

7

public class User implements Comparable<User> {

    // ...

     

    public int compareTo(User o) {

        return this.getName().compareToIgnoreCase(o.getName());

    }

}

 

現在,讓我們斷言兩個具有相同名稱的用戶是等效的:

1

2

3

4

5

6

7

8

9

10

@Test

public void whenComparingUsers_thenEquivalent() {

    User aUser = new User();

    aUser.setName("John Doe");

 

    User anotherUser = new User();

    anotherUser.setName("john doe");

 

    assertThat(aUser).isEquivalentAccordingToCompareTo(anotherUser);

}

 

5.8。迭代斷言

除了聲明Iterable實例的大小(無論它是空的還是沒有重複的)外,對Iterable的最典型的聲明是它包含一些元素:

1

2

3

4

5

6

@Test

public void whenCheckingIterable_thenContains() {

    List<Integer> aList = Arrays.asList(4, 5, 6);

 

    assertThat(aList).contains(5);

}

 

它包含另一個Iterable的任何元素:

1

2

3

4

5

6

@Test

public void whenCheckingIterable_thenContainsAnyInList() {

    List<Integer> aList = Arrays.asList(1, 2, 3);

 

    assertThat(aList).containsAnyIn(Arrays.asList(1, 5, 10));

}

 

並且該主題具有相同的元素,並且順序相同,就像另一個元素一樣:

1

2

3

4

5

6

7

8

9

@Test

public void whenCheckingIterable_thenContainsExactElements() {

    List<String> aList = Arrays.asList("10", "20", "30");

    List<String> anotherList = Arrays.asList("10", "20", "30");

 

    assertThat(aList)

      .containsExactlyElementsIn(anotherList)

      .inOrder();

}

 

並使用自定義比較器訂購:

1

2

3

4

5

6

7

8

9

@Test

public void givenComparator_whenCheckingIterable_thenOrdered() {

    Comparator<String> aComparator

      = (a, b) -> new Float(a).compareTo(new Float(b));

 

    List<String> aList = Arrays.asList("1", "012", "0020", "100");

 

    assertThat(aList).isOrdered(aComparator);

}

 

5.9。Map斷言

除了斷言Map實例是否爲空或具有特定大小之外,我們可以檢查它是否具有特定條目:

1

2

3

4

5

6

7

@Test

public void whenCheckingMap_thenContainsEntry() {

    Map<String, Object> aMap = new HashMap<>();

    aMap.put("one", 1L);

 

    assertThat(aMap).containsEntry("one", 1L);

}

 

如果它具有特定的密鑰:

1

2

3

4

5

6

@Test

public void whenCheckingMap_thenContainsKey() {

    // ...

 

    assertThat(map).containsKey("one");

}

 

或者它具有與另一個Map相同的條目:

1

2

3

4

5

6

7

8

9

10

11

@Test

public void whenCheckingMap_thenContainsEntries() {

    Map<String, Object> aMap = new HashMap<>();

    aMap.put("first", 1L);

    aMap.put("second", 2.0);

    aMap.put("third", 3f);

 

    Map<String, Object> anotherMap = new HashMap<>(aMap);

 

    assertThat(aMap).containsExactlyEntriesIn(anotherMap);

}

 

5.10。異常斷言

僅爲異常對象提供了兩種重要的方法。

我們可以編寫針對異常原因的斷言:

1

2

3

4

5

6

7

8

9

@Test

public void whenCheckingException_thenInstanceOf() {

    Exception anException

      = new IllegalArgumentException(new NumberFormatException());

 

    assertThat(anException)

      .hasCauseThat()

      .isInstanceOf(NumberFormatException.class);

}

 

或其訊息:

1

2

3

4

5

6

7

8

9

@Test

public void whenCheckingException_thenCauseMessageIsKnown() {

    Exception anException

      = new IllegalArgumentException("Bad value");

 

    assertThat(anException)

      .hasMessageThat()

      .startsWith("Bad");

}

 

5.11。類斷言

對於類斷言,只有一種重要的方法可以用來測試一個類是否可分配給另一個:

1

2

3

4

5

6

@Test

public void whenCheckingClass_thenIsAssignable() {

    Class<Double> aClass = Double.class;

 

    assertThat(aClass).isAssignableTo(Number.class);

}

 

6. Java 8斷言

可選和流是Truth支持的僅有的兩種Java 8類型。

6.1。可選斷言

有三種重要的方法可以驗證Optional。

我們可以測試它是否具有特定的值:

1

2

3

4

5

6

@Test

public void whenCheckingJavaOptional_thenHasValue() {

    Optional<Integer> anOptional = Optional.of(1);

 

    assertThat(anOptional).hasValue(1);

}

 

如果存在該值:

1

2

3

4

5

6

@Test

public void whenCheckingJavaOptional_thenPresent() {

    Optional<String> anOptional = Optional.of("Baeldung");

 

    assertThat(anOptional).isPresent();

}

 

或如果不存在該值:

1

2

3

4

5

6

@Test

public void whenCheckingJavaOptional_thenEmpty() {

    Optional anOptional = Optional.empty();

 

    assertThat(anOptional).isEmpty();

}

 

6.2。流斷言

Stream的斷言與Iterable的斷言非常相似。

例如,我們可以測試特定的Stream是否包含相同順序的Iterable的所有對象:

1

2

3

4

5

6

7

8

@Test

public void whenCheckingStream_thenContainsInOrder() {

    Stream<Integer> anStream = Stream.of(1, 2, 3);

 

    assertThat(anStream)

      .containsAllOf(1, 2, 3)

      .inOrder();

}

 

有關更多示例,請參考Iterable Assertions部分。

7.Guava斷言

在本節中,我們將看到Truth中支持的Guava類型的斷言示例。

7.1。可選斷言

對於Guava Optional也有三種重要的斷言方法。該hasValue的()和isPresent()方法的行爲完全一樣與Java 8 可選。

但不是的isEmpty()斷言一個可選的不存在,我們使用isAbsent() :

1

2

3

4

5

6

@Test

public void whenCheckingGuavaOptional_thenIsAbsent() {

    Optional anOptional = Optional.absent();

 

    assertThat(anOptional).isAbsent();

}

 

7.2。多圖斷言

Multimap和標準Map斷言非常相似。

一個顯着的區別是,我們可以在Multimap中獲取鍵的多個值,並對這些值進行斷言。

這是一個示例,測試“ one”鍵的值是否具有兩個大小:

1

2

3

4

5

6

7

8

9

10

@Test

public void whenCheckingGuavaMultimap_thenExpectedSize() {

    Multimap<String, Object> aMultimap = ArrayListMultimap.create();

    aMultimap.put("one", 1L);

    aMultimap.put("one", 2.0);

 

    assertThat(aMultimap)

      .valuesForKey("one")

      .hasSize(2);

}

 

有關更多示例,請參考“ Map斷言”部分。

7.3。多對象斷言

多對象對象的斷言包括用於Iterable的斷言,以及一種用於驗證鍵是否具有特定出現次數的額外方法:

1

2

3

4

5

6

7

@Test

public void whenCheckingGuavaMultiset_thenExpectedCount() {

    TreeMultiset<String> aMultiset = TreeMultiset.create();

    aMultiset.add("baeldung", 10);

 

    assertThat(aMultiset).hasCount("baeldung", 10);

}

 

7.4。表斷言

除了檢查它的大小或它的空白處,我們還可以檢查一個表以驗證它是否包含給定行和列的特定映射:

1

2

3

4

5

6

7

@Test

public void whenCheckingGuavaTable_thenContains() {

    Table<String, String, String> aTable = TreeBasedTable.create();

    aTable.put("firstRow", "firstColumn", "baeldung");

 

    assertThat(aTable).contains("firstRow", "firstColumn");

}

 

或者它包含特定的單元格:

1

2

3

4

5

6

@Test

public void whenCheckingGuavaTable_thenContainsCell() {

    Table<String, String, String> aTable = getDummyGuavaTable();

 

    assertThat(aTable).containsCell("firstRow", "firstColumn", "baeldung");

}

 

此外,我們可以檢查它是否包含給定的行,列或值。請參閱相關測試用例的源代碼。

8.自定義失敗消息和標籤

當斷言失敗時,Truth會顯示易讀的消息,準確指出問題所在。但是,有時有必要向這些消息添加更多信息,以提供有關所發生情況的更多詳細信息。

Truth使我們能夠自定義那些失敗消息:

1

2

3

4

5

6

@Test

public void whenFailingAssertion_thenCustomMessage() {

    assertWithMessage("TEST-985: Secret user subject was NOT null!")

      .that(new User())

      .isNull();

}

 

運行測試後,我們得到以下輸出:

1

2

TEST-985: Secret user subject was NOT null!:

  Not true that <com.baeldung.testing.truth.User@ae805d5e> is null

 

同樣,我們可以添加一個自定義標籤,該標籤將在錯誤消息中的主題之前顯示。當對象沒有有用的字符串表示形式時,這可能會派上用場:

1

2

3

4

5

6

7

8

@Test

public void whenFailingAssertion_thenMessagePrefix() {

    User aUser = new User();

 

    assertThat(aUser)

      .named("User [%s]", aUser.getName())

      .isNull();

}

 

如果運行測試,我們將看到以下輸出:

1

2

Not true that User [John Doe]

  (<com.baeldung.testing.truth.User@ae805d5e>) is null

 

9.擴展

擴展Truth意味着我們可以添加對自定義類型的支持。爲此,我們需要創建一個類:

  • 擴展Subject類或其子類之一

  • 定義一個接受兩個參數的構造函數– FailureStrategy和我們的自定義類型的實例

  • 聲明一個SubjectFactory類型的字段,Truth將使用該字段創建我們自定義主題的實例

  • 實現一個靜態的assertThat()方法,該方法接受我們的自定義類型

  • 公開我們的測試斷言API

現在我們知道了如何擴展Truth,讓我們創建一個添加對User類型的對象的支持的類:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

public class UserSubject

  extends ComparableSubject<UserSubject, User> {

 

    private UserSubject(

      FailureStrategy failureStrategy, User target) {

        super(failureStrategy, target);

    }

 

    private static final

      SubjectFactory<UserSubject, User> USER_SUBJECT_FACTORY

      = new SubjectFactory<UserSubject, User>() {

 

        public UserSubject getSubject(

          FailureStrategy failureStrategy, User target) {

            return new UserSubject(failureStrategy, target);

        }

    };

 

    public static UserSubject assertThat(User user) {

        return Truth.assertAbout(USER_SUBJECT_FACTORY).that(user);

    }

 

    public void hasName(String name) {

        if (!actual().getName().equals(name)) {

            fail("has name", name);

        }

    }

 

    public void hasNameIgnoringCase(String name) {

        if (!actual().getName().equalsIgnoreCase(name)) {

            fail("has name ignoring case", name);

        }

    }

 

    public IterableSubject emails() {

        return Truth.assertThat(actual().getEmails());

    }

}

 

現在,我們可以靜態導入自定義主題的assertThat()方法並編寫一些測試:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

@Test

public void whenCheckingUser_thenHasName() {

    User aUser = new User();

 

    assertThat(aUser).hasName("John Doe");

}

 

@Test

public void whenCheckingUser_thenHasNameIgnoringCase() {

    // ...

 

    assertThat(aUser).hasNameIgnoringCase("john doe");

}

 

@Test

public void givenUser_whenCheckingEmails_thenExpectedSize() {

    // ...

 

    assertThat(aUser)

      .emails()

      .hasSize(2);

}

 

10.結論

在本教程中,我們探索了Truth給我們編寫更具可讀性的測試和失敗消息的可能性。

我們展示了支持Java和番石榴類型,定製的失敗消息最流行的斷言方法,並擴展Truth定製主題。

與往常一樣,可以在Github上找到本文的完整源代碼。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章