Google Truth로 테스트

1. 개요

Truth테스트 어설 션 및 실패 메시지를 더 쉽게 읽을 수 있도록 설계된 유창하고 유연한 오픈 소스 테스트 프레임 워크입니다.

이 기사에서는 Truth 프레임 워크 의 주요 기능을 살펴보고 그 기능을 보여주는 예제를 구현할 것입니다.

2. Maven 종속성

먼저 pom.xml에 truthtruth-java8-extension 을 추가해야합니다 .

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

Maven Central에서 최신 버전의 truth 및 truth-java8-extension을 찾을 수 있습니다.

3. 소개

Truth를 사용하면 다양한 클래스에 대해 읽을 수있는 어설 션 및 실패 메시지를 작성할 수 있습니다.

  • 표준 Java – 프리미티브, 배열, 문자열, 객체, 컬렉션, throwables, 클래스 등
  • Java 8옵션스트림 인스턴스
  • GuavaOptional , Multimap , MultisetTable 객체
  • 사용자 정의 유형 – 나중에 보게 될 주제 클래스 를 확장하여

관통 진실Truth8 클래스 라이브러리는에서 작동한다는 주장을 작성하기위한 유틸리티 메소드 제공 주체 , 즉 테스트중인 값이나 객체의를.

주제가 알려지면 Truth 는 컴파일 타임에 해당 주제에 대해 알려진 명제에 대해 추론 할 수 있습니다 . 이를 통해 특정 주제에 특정한 명제 메서드를 선언하는 값 주위에 래퍼를 반환 할 수 있습니다.

예를 들어, 목록에서 주장 할 때 Truth 는 다른 것 중에서 contains ()containsAnyOf () 와 같은 메서드를 정의 하는 IterableSubject 인스턴스를 반환합니다 . Map 에서 주장 할 때 containsEntry ()containsKey () 와 같은 메서드를 선언 하는 MapSubject 를 반환합니다 .

4. 시작하기

어설 션 작성을 시작하려면 먼저 Truth 의 진입 점을 가져 오겠습니다 .

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

이제 다음 몇 가지 예제에서 사용할 간단한 클래스를 작성해 보겠습니다.

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 }

사용자 정의 equals () 메소드에 주목하십시오 . 여기서 두 User 객체는 이름이 같으면 동일합니다.

5. 표준 자바 어설 션

이 섹션에서는 표준 Java 유형에 대한 테스트 어설 션을 작성하는 방법에 대한 자세한 예제를 볼 수 있습니다.

5.1. 개체 어설 션

Truth 는 개체에 대한 주장을 수행하기위한 Subject 래퍼를 제공 합니다. Subject 는 라이브러리에있는 다른 모든 래퍼의 부모이기도하며 Object , 우리의 경우 User 가 다른 개체와 같은지 확인하기위한 메서드를 선언 합니다.

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

또는 목록의 주어진 객체와 같은 경우 :

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

또는 그렇지 않은 경우 :

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

null 여부 :

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

또는 특정 클래스의 인스턴스 인 경우 :

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

Subject 클래스 에는 다른 assertion 메서드가 있습니다 . 모두 발견하려면 주제 문서를 참조하십시오 .

다음 섹션에서는 Truth가 지원하는 각 특정 유형에 대해 가장 관련성이 높은 방법에 초점을 맞출 것 입니다. 그러나 Subject 클래스의 모든 메서드 도 적용 할 수 있습니다.

5.2. Integer , FloatDouble Assertions

Integer , FloatDouble 인스턴스가 같은지 비교할 수 있습니다.

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

더 큰 경우 :

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

이하 :

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

또한 FloatDouble 인스턴스를 검사하여 예상 정밀도 내에 있는지 여부를 확인할 수도 있습니다.

@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. BigDecimal 어설 션

일반적인 단언 외에도이 유형은 규모를 무시하고 비교할 수 있습니다.

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

5.4. 부울 어설 션

두 개의 관련 메서드, isTrue ()isFalse () 만 제공됩니다 .

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

5.5. 문자열 어설 션

문자열 이 특정 텍스트로 시작 하는지 테스트 할 수 있습니다 .

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

또한 문자열에 주어진 문자열이 포함되어 있는지, 예상 값으로 끝나는 지 또는 비어 있는지 확인할 수 있습니다. 이러한 방법 및 기타 방법에 대한 테스트 사례는 소스 코드에서 사용할 수 있습니다.

5.6. 어레이 어설 션

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

이 튜토리얼에서 우리는 Truth 가 더 읽기 쉬운 테스트와 실패 메시지를 작성할 수있는 가능성을 탐구했습니다 .

지원되는 Java 및 Guava 유형, 사용자 정의 된 실패 메시지 및 사용자 정의 주제로 확장 된 Truth에 대한 가장 인기있는 어설 션 방법을 소개했습니다 .

항상 그렇듯이이 기사의 전체 소스 코드는 Github에서 찾을 수 있습니다.