Menguji dengan Google Truth

1. Ikhtisar

Truth adalah kerangka kerja pengujian sumber terbuka yang lancar dan fleksibel yang dirancang untuk membuat pernyataan pengujian dan pesan kegagalan lebih mudah dibaca.

Dalam artikel ini, kita akan mempelajari fitur utama framework Truth dan menerapkan contoh untuk menunjukkan kemampuannya.

2. Ketergantungan Maven

Pertama, kita perlu menambahkan kebenaran dan kebenaran-java8-ekstensi ke pom.xml kita :

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

Anda dapat menemukan versi kebenaran dan kebenaran-java8-ekstensi terbaru di Maven Central.

3. Pendahuluan

Truth memungkinkan kita untuk menulis pernyataan yang dapat dibaca dan pesan kegagalan untuk berbagai kelas:

  • Java Standar - primitif, array, string, objek, koleksi, throwable, kelas, dll.
  • Java 8 - Opsional dan Streaming contoh
  • Guava - Opsional , Multimap , Multiset , dan objek Tabel
  • Jenis khusus - dengan memperluas kelas Subjek , seperti yang akan kita lihat nanti

Melalui class Truth dan Truth8 , library menyediakan metode utilitas untuk menulis pernyataan yang berfungsi pada subjek , yaitu nilai atau objek yang diuji.

Setelah subjek diketahui, Kebenaran dapat bernalar pada waktu kompilasi tentang proposisi apa yang diketahui untuk subjek tersebut . Ini memungkinkannya untuk mengembalikan pembungkus di sekitar nilai kita yang menyatakan metode proposisi khusus untuk subjek tertentu itu.

Misalnya, saat menegaskan pada daftar, Truth mengembalikan instance IterableSubject yang mendefinisikan metode seperti contains () dan containsAnyOf () , antara lain. Saat menegaskan pada Map , ia mengembalikan MapSubject yang mendeklarasikan metode seperti containsEntry () dan containsKey () .

4. Memulai

Untuk mulai menulis pernyataan, pertama-tama impor titik masuk Truth :

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

Sekarang, mari kita tulis kelas sederhana yang akan kita gunakan dalam beberapa contoh berikut:

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 }

Perhatikan metode custom equals () , di mana kami menyatakan bahwa dua objek User adalah sama jika namanya sama.

5. Pernyataan Java Standar

Di bagian ini, kita akan melihat contoh mendetail tentang cara menulis pernyataan pengujian untuk tipe Java standar.

5.1. Pernyataan Objek

Truth menyediakan pembungkus Subjek untuk melakukan pernyataan pada objek. Subjek juga merupakan induk dari semua pembungkus lain di perpustakaan dan mendeklarasikan metode untuk menentukan apakah sebuah Objek , dalam kasus kami Pengguna , sama dengan objek lain:

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

atau jika itu sama dengan objek tertentu dalam daftar:

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

atau jika tidak:

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

jika nol atau tidak:

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

atau jika itu adalah turunan dari kelas tertentu:

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

Ada metode pernyataan lain di kelas Subjek . Untuk menemukan semuanya, lihat dokumentasi Subjek .

Di bagian berikut, kita akan fokus pada metode yang paling relevan untuk setiap jenis dukungan Truth tertentu . Namun, perlu diingat bahwa semua metode di kelas Subjek juga dapat diterapkan.

5.2. Integer , Float, dan Double Assertions

Instans Integer , Float, dan Double dapat dibandingkan untuk persamaan:

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

jika mereka lebih besar:

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

atau lebih kecil:

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

Selanjutnya , instans Float dan Double juga dapat diperiksa untuk melihat apakah mereka berada dalam presisi yang diharapkan atau tidak:

@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. Pernyataan BigDecimal

Selain pernyataan umum, tipe ini dapat dibandingkan dengan mengabaikan skalanya:

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

5.4. Pernyataan Boolean

Hanya dua metode relevan yang disediakan, isTrue () dan isFalse () :

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

5.5. Pernyataan String

Kita dapat menguji apakah sebuah String dimulai dengan teks tertentu:

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

Selain itu, kita dapat memeriksa apakah string berisi String yang diberikan, apakah diakhiri dengan nilai yang diharapkan atau kosong. Kasus uji untuk ini dan metode lainnya tersedia di kode sumber.

5.6. Array Assertions

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

Dalam tutorial ini, kami menjelajahi kemungkinan yang diberikan Kebenaran kepada kami untuk menulis tes yang lebih mudah dibaca dan pesan kegagalan.

Kami memamerkan metode pernyataan paling populer untuk jenis Java dan Guava yang didukung, pesan kegagalan yang disesuaikan, dan Truth yang diperluas dengan subjek khusus.

Seperti biasa, kode sumber lengkap untuk artikel ini dapat ditemukan di Github.