Thử nghiệm với Google Truth

1. Khái quát chung

Truth là một khung kiểm tra mã nguồn mở thông thạo và linh hoạt được thiết kế để làm cho các xác nhận kiểm tra và thông báo lỗi dễ đọc hơn.

Trong bài viết này, chúng ta sẽ khám phá các tính năng chính của khung Truth và triển khai các ví dụ để giới thiệu các khả năng của nó.

2. Sự phụ thuộc của Maven

Đầu tiên, chúng ta cần thêm những sự thậtsự thật java8-mở rộng để chúng tôi pom.xml:

 com.google.truth truth 0.32   com.google.truth.extensions truth-java8-extension 0.32 test 

Bạn có thể tìm thấy các phiên bản mới nhất của phần mở rộng Truth và truth-java8 trên Maven Central.

3. Giới thiệu

Truth cho phép chúng ta viết các xác nhận có thể đọc được và thông báo lỗi cho nhiều lớp khác nhau:

  • Java tiêu chuẩn - nguyên thủy, mảng, chuỗi, đối tượng, tập hợp, vật có thể ném, lớp, v.v.
  • Java 8 - Tùy chọnSuối trường
  • Guava - Các đối tượng tùy chọn , Multimap , MultisetTable
  • Các loại tùy chỉnh - bằng cách mở rộng lớp Chủ đề , như chúng ta sẽ thấy sau

Thông qua các lớp TruthTruth8 , thư viện cung cấp các phương thức tiện ích để viết các xác nhận hoạt động trên một chủ đề , đó là giá trị hoặc đối tượng đang được kiểm tra.

Một khi chủ đề được biết đến, Truth có thể lập luận tại thời điểm tổng hợp về những mệnh đề nào được biết đến đối với chủ đề đó . Điều này cho phép nó trả về các trình bao bọc xung quanh giá trị của chúng tôi để khai báo các phương thức mệnh đề cụ thể cho chủ đề cụ thể đó.

Ví dụ: khi xác nhận trên một danh sách, Truth trả về một cá thể IterableSubject xác định các phương thức như chứa ()containsAnyOf () , trong số các phương thức khác. Khi xác nhận trên một Bản đồ , nó trả về một MapSubject khai báo các phương thức như containsEntry ()containsKey () .

4. Bắt đầu

Để bắt đầu viết xác nhận, trước tiên hãy nhập các điểm nhập của Truth :

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

Bây giờ, hãy viết một lớp đơn giản mà chúng ta sẽ sử dụng trong một vài ví dụ sau:

public class User { private String name = "John Doe"; private List 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 }

Lưu ý phương thức equals () tùy chỉnh , trong đó chúng tôi nói rằng hai đối tượng Người dùng là bằng nhau nếu tên của chúng là.

5. Xác định Java tiêu chuẩn

Trong phần này, chúng ta sẽ xem các ví dụ chi tiết về cách viết các xác nhận kiểm tra cho các kiểu Java tiêu chuẩn.

5.1. Xác định đối tượng

Truth cung cấp trình bao bọc Chủ đề để thực hiện các xác nhận trên các đối tượng. Chủ đề cũng là cha của tất cả các trình bao bọc khác trong thư viện và khai báo các phương thức để xác định xem một Đối tượng , trong trường hợp của chúng tôi là Người dùng , có ngang bằng với một đối tượng khác hay không:

@Test public void whenComparingUsers_thenEqual() { User aUser = new User("John Doe"); User anotherUser = new User("John Doe"); assertThat(aUser).isEqualTo(anotherUser); }

hoặc nếu nó bằng một đối tượng nhất định trong danh sách:

@Test public void whenComparingUser_thenInList() { User aUser = new User(); assertThat(aUser).isIn(Arrays.asList(1, 3, aUser, null)); }

hoặc nếu nó không phải là:

@Test public void whenComparingUser_thenNotInList() { // ... assertThat(aUser).isNotIn(Arrays.asList(1, 3, "Three")); }

nếu nó rỗng hay không:

@Test public void whenComparingUser_thenIsNull() { User aUser = null; assertThat(aUser).isNull(); } @Test public void whenComparingUser_thenNotNull() { User aUser = new User(); assertThat(aUser).isNotNull(); }

hoặc nếu đó là một phiên bản của một lớp cụ thể:

@Test public void whenComparingUser_thenInstanceOf() { // ... assertThat(aUser).isInstanceOf(User.class); }

Có các phương thức khẳng định khác trong lớp Chủ đề . Để khám phá tất cả, hãy tham khảo tài liệu Chủ đề .

Trong các phần tiếp theo, chúng ta sẽ tập trung vào các phương pháp phù hợp nhất cho từng loại Truth hỗ trợ cụ thể . Tuy nhiên, hãy nhớ rằng tất cả các phương pháp trong lớp Chủ đề cũng có thể được áp dụng.

5.2. Xác định số nguyên , số nổi và xác định kép

Các phiên bản Integer , FloatDouble có thể được so sánh cho bằng nhau:

@Test public void whenComparingInteger_thenEqual() { int anInt = 10; assertThat(anInt).isEqualTo(10); }

nếu chúng lớn hơn:

@Test public void whenComparingFloat_thenIsBigger() { float aFloat = 10.0f; assertThat(aFloat).isGreaterThan(1.0f); }

hoặc nhỏ hơn:

@Test public void whenComparingDouble_thenIsSmaller() { double aDouble = 10.0f; assertThat(aDouble).isLessThan(20.0); }

Hơn nữa , các phiên bản FloatDouble cũng có thể được kiểm tra để xem chúng có nằm trong độ chính xác mong đợi hay không:

@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. Xác định BigDecimal

Bên cạnh các khẳng định thông thường, loại này có thể được so sánh khi bỏ qua quy mô của nó:

@Test public void whenComparingBigDecimal_thenEqualIgnoringScale() { BigDecimal aBigDecimal = BigDecimal.valueOf(1000, 3); assertThat(aBigDecimal).isEqualToIgnoringScale(new BigDecimal(1.0)); }

5.4. Khẳng định Boolean

Chỉ có hai phương thức liên quan được cung cấp, isTrue ()isFalse () :

@Test public void whenCheckingBoolean_thenTrue() { boolean aBoolean = true; assertThat(aBoolean).isTrue(); }

5.5. Xác nhận chuỗi

Chúng tôi có thể kiểm tra xem một Chuỗi có bắt đầu bằng một văn bản cụ thể hay không:

@Test public void whenCheckingString_thenStartsWith() { String aString = "This is a string"; assertThat(aString).startsWith("This"); }

Ngoài ra, chúng ta có thể kiểm tra xem chuỗi có chứa một Chuỗi đã cho hay không, nếu nó kết thúc bằng một giá trị mong đợi hoặc nó có trống hay không. Các trường hợp thử nghiệm cho các phương pháp này và các phương pháp khác có sẵn trong mã nguồn.

5.6. Xác định mảng

We can check Arrays to see if they are equal to other arrays:

@Test public void whenComparingArrays_thenEqual() { String[] firstArrayOfStrings = { "one", "two", "three" }; String[] secondArrayOfStrings = { "one", "two", "three" }; assertThat(firstArrayOfStrings).isEqualTo(secondArrayOfStrings); }

or if they are empty:

@Test public void whenCheckingArray_thenEmpty() { Object[] anArray = {}; assertThat(anArray).isEmpty(); }

5.7. Comparable Assertions

Besides testing whether a Comparable is greater than or less than another instance, we can check to see if they are at least a given value:

@Test public void whenCheckingComparable_thenAtLeast() { Comparable aComparable = 5; assertThat(aComparable).isAtLeast(1); }

Also, we can test whether they are within a particular range:

@Test public void whenCheckingComparable_thenInRange() { // ... assertThat(aComparable).isIn(Range.closed(1, 10)); }

or in a particular list:

@Test public void whenCheckingComparable_thenInList() { // ... assertThat(aComparable).isIn(Arrays.asList(4, 5, 6)); }

We can also test if two Comparable instances are equivalent according to the class's compareTo() method.

First, let's modify our User class to implement the Comparable interface:

public class User implements Comparable { // ... public int compareTo(User o) { return this.getName().compareToIgnoreCase(o.getName()); } }

Now, let's assert that two users with the same name are equivalent:

@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 Assertions

In addition to asserting the size of an Iterable instance, whether it's empty or has no duplicates, most typical assertions on an Iterable are that it contains some element:

@Test public void whenCheckingIterable_thenContains() { List aList = Arrays.asList(4, 5, 6); assertThat(aList).contains(5); }

that it contains any element of another Iterable:

@Test public void whenCheckingIterable_thenContainsAnyInList() { List aList = Arrays.asList(1, 2, 3); assertThat(aList).containsAnyIn(Arrays.asList(1, 5, 10)); }

and that the subject has the same elements, in the same order, like another:

@Test public void whenCheckingIterable_thenContainsExactElements() { List aList = Arrays.asList("10", "20", "30"); List anotherList = Arrays.asList("10", "20", "30"); assertThat(aList) .containsExactlyElementsIn(anotherList) .inOrder(); }

and if it's ordered using a custom comparator:

@Test public void givenComparator_whenCheckingIterable_thenOrdered() { Comparator aComparator = (a, b) -> new Float(a).compareTo(new Float(b)); List aList = Arrays.asList("1", "012", "0020", "100"); assertThat(aList).isOrdered(aComparator); }

5.9. Map Assertions

In addition to asserting that a Map instance is empty or not, or has a specific size; we can check if it has a specific entry:

@Test public void whenCheckingMap_thenContainsEntry() { Map aMap = new HashMap(); aMap.put("one", 1L); assertThat(aMap).containsEntry("one", 1L); }

if it has a specific key:

@Test public void whenCheckingMap_thenContainsKey() { // ... assertThat(map).containsKey("one"); }

or if it has the same entries as another Map:

@Test public void whenCheckingMap_thenContainsEntries() { Map aMap = new HashMap(); aMap.put("first", 1L); aMap.put("second", 2.0); aMap.put("third", 3f); Map anotherMap = new HashMap(aMap); assertThat(aMap).containsExactlyEntriesIn(anotherMap); }

5.10. Exception Assertions

Only two methods of importance are provided for Exception objects.

We can write assertions addressed to the cause of the exception:

@Test public void whenCheckingException_thenInstanceOf() { Exception anException = new IllegalArgumentException(new NumberFormatException()); assertThat(anException) .hasCauseThat() .isInstanceOf(NumberFormatException.class); }

or to its message:

@Test public void whenCheckingException_thenCauseMessageIsKnown() { Exception anException = new IllegalArgumentException("Bad value"); assertThat(anException) .hasMessageThat() .startsWith("Bad"); }

5.11. Class Assertions

There's only one important method for Class assertions with which we can test whether a class is assignable to another:

@Test public void whenCheckingClass_thenIsAssignable() { Class aClass = Double.class; assertThat(aClass).isAssignableTo(Number.class); }

6. Java 8 Assertions

Optional and Stream are the only two Java 8 types that Truth supports.

6.1. Optional Assertions

There are three important methods to verify an Optional.

We can test whether it has a particular value:

@Test public void whenCheckingJavaOptional_thenHasValue() { Optional anOptional = Optional.of(1); assertThat(anOptional).hasValue(1); }

if the value is present:

@Test public void whenCheckingJavaOptional_thenPresent() { Optional anOptional = Optional.of("Baeldung"); assertThat(anOptional).isPresent(); }

or if the value is not present:

@Test public void whenCheckingJavaOptional_thenEmpty() { Optional anOptional = Optional.empty(); assertThat(anOptional).isEmpty(); }

6.2. Stream Assertions

Assertions for a Stream are very similar to the ones for an Iterable.

For example, we can test if a particular Stream contains all objects of an Iterable in the same order:

@Test public void whenCheckingStream_thenContainsInOrder() { Stream anStream = Stream.of(1, 2, 3); assertThat(anStream) .containsAllOf(1, 2, 3) .inOrder(); }

For more examples, please refer to the Iterable Assertions section.

7. Guava Assertions

In this section, we'll see examples of assertions for the supported Guava types in Truth.

7.1. Optional Assertions

There are also three important assertion methods for a Guava Optional. The hasValue() and isPresent() methods behave exactly as with a Java 8 Optional.

But instead of isEmpty() to assert that an Optional is not present, we use isAbsent():

@Test public void whenCheckingGuavaOptional_thenIsAbsent() { Optional anOptional = Optional.absent(); assertThat(anOptional).isAbsent(); }

7.2. Multimap Assertions

Multimap and standard Map assertions are very similar.

One notable difference is that we can get the multiple values of a key within a Multimap and make assertions on those values.

Here's an example that tests if the values of the “one” key have a size of two:

@Test public void whenCheckingGuavaMultimap_thenExpectedSize() { Multimap aMultimap = ArrayListMultimap.create(); aMultimap.put("one", 1L); aMultimap.put("one", 2.0); assertThat(aMultimap) .valuesForKey("one") .hasSize(2); }

For more examples, please refer to the Map Assertions section.

7.3. Multiset Assertions

Assertions for Multiset objects include the ones for an Iterable and one extra method to verify if a key has a particular number of occurrences:

@Test public void whenCheckingGuavaMultiset_thenExpectedCount() { TreeMultiset aMultiset = TreeMultiset.create(); aMultiset.add("baeldung", 10); assertThat(aMultiset).hasCount("baeldung", 10); }

7.4. Table Assertions

Besides checking its size or where it's empty, we can check a Table to verify if it contains a particular mapping for a given row and column:

@Test public void whenCheckingGuavaTable_thenContains() { Table aTable = TreeBasedTable.create(); aTable.put("firstRow", "firstColumn", "baeldung"); assertThat(aTable).contains("firstRow", "firstColumn"); }

or if it contains a particular cell:

@Test public void whenCheckingGuavaTable_thenContainsCell() { Table aTable = getDummyGuavaTable(); assertThat(aTable).containsCell("firstRow", "firstColumn", "baeldung"); }

Furthermore, we can check if it contains a given row, column, or value. See the source code for the relevant test cases.

8. Custom Failure Messages and Labels

When an assertion fails, Truth displays very readable messages denoting exactly what went wrong. However, sometimes is necessary to add more information to those messages to provide more details about what happened.

Truth allows us to customize those failure messages:

@Test public void whenFailingAssertion_thenCustomMessage() { assertWithMessage("TEST-985: Secret user subject was NOT null!") .that(new User()) .isNull(); }

After running the test, we get the following output:

TEST-985: Secret user subject was NOT null!: Not true that <[email protected]> is null

Also, we can add a custom label that gets displayed before our subject in error messages. This may come in handy when an object does not have a helpful string representation:

@Test public void whenFailingAssertion_thenMessagePrefix() { User aUser = new User(); assertThat(aUser) .named("User [%s]", aUser.getName()) .isNull(); }

If we run the test, we can see the following output:

Not true that User [John Doe] (<[email protected]>) is null

9. Extensions

Extending Truth means we can add support for custom types. To do this, we need to create a class that:

  • extends the Subject class or one of its subclasses
  • defines a constructor that accepts two arguments – a FailureStrategy and an instance of our custom type
  • declares a field of SubjectFactory type, which Truth will use to create instances of our custom subject
  • implements a static assertThat() method that accepts our custom type
  • exposes our test assertion API

Now that we know how to extend Truth, let's create a class that adds support for objects of type User:

public class UserSubject extends ComparableSubject { private UserSubject( FailureStrategy failureStrategy, User target) { super(failureStrategy, target); } private static final SubjectFactory USER_SUBJECT_FACTORY = new SubjectFactory() { 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()); } }

Now, we can statically import the assertThat() method of our custom subject and write some tests:

@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. Conclusion

Trong hướng dẫn này, chúng tôi đã khám phá các khả năng mà Sự thật mang lại cho chúng tôi để viết các bài kiểm tra và thông báo thất bại dễ đọc hơn.

Chúng tôi giới thiệu các phương pháp xác nhận phổ biến nhất cho các loại Java và Guava được hỗ trợ, thông báo lỗi được tùy chỉnh và Sự thật mở rộng với các chủ đề tùy chỉnh.

Như mọi khi, bạn có thể tìm thấy mã nguồn hoàn chỉnh cho bài viết này trên Github.