1. 概要

Hamcrest は、Javaエコシステムでの単体テストに使用されるよく知られたフレームワークです。 これはJUnitにバンドルされており、簡単に言えば、アサーションを作成するために、マッチャークラスと呼ばれる既存の述語を使用します。

このチュートリアルでは、 Hamcrest API を探索し、それを利用してソフトウェアのより簡潔で直感的な単体テストを作成する方法を学習します。

2. ハムクレストのセットアップ

pom.xml ファイルに次の依存関係を追加することで、MavenでHamcrestを使用できます。

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>

このライブラリの最新バージョンは、常にここにあります。

3. テスト例

Hamcrest は、アサーションを作成するためのjunitおよびその他のテストフレームワークで一般的に使用されます。 具体的には、junitの多数のassertメソッドを使用する代わりに、APIの単一のassertThatステートメントと適切なマッチャーのみを使用します。

大文字と小文字に関係なく、2つのStringが等しいかどうかをテストする例を見てみましょう。 これにより、Hamcrestがテスト方法にどのように適合するかについて明確なアイデアが得られるはずです。

public class StringMatcherTest {
    
    @Test
    public void given2Strings_whenEqual_thenCorrect() {
        String a = "foo";
        String b = "FOO";
        assertThat(a, equalToIgnoringCase(b));
    }
}

次のセクションでは、他のいくつかの一般的なマッチャーHamcrestが提供するものを見ていきます。

4. オブジェクトマッチャー

Hamcrest は、任意のJavaオブジェクトでアサーションを作成するためのマッチャーを提供します。

ObjecttoStringメソッドが、指定された Stringを返すことを表明するには:

@Test
public void givenBean_whenToStringReturnsRequiredString_thenCorrect(){
    Person person=new Person("Barrack", "Washington");
    String str=person.toString();
    assertThat(person,hasToString(str));
}

また、あるクラスが別のクラスのサブクラスであることを確認することもできます。

@Test
public void given2Classes_whenOneInheritsFromOther_thenCorrect(){
        assertThat(Cat.class,typeCompatibleWith(Animal.class));
    }
}

5. ビーンマッチャー

Hamcrest のBeanマッチャーを使用して、JavaBeanのプロパティを検査できます。

次のPersonBeanを想定します。

public class Person {
    String name;
    String address;

    public Person(String personName, String personAddress) {
        name = personName;
        address = personAddress;
    }
}

Beanにnameというプロパティがあるかどうかは、次のように確認できます。

@Test
public void givenBean_whenHasValue_thenCorrect() {
    Person person = new Person("Baeldung", 25);
    assertThat(person, hasProperty("name"));
}

Person アドレスプロパティを持ち、ニューヨークに初期化されているかどうかを確認することもできます。

@Test
public void givenBean_whenHasCorrectValue_thenCorrect() {
    Person person = new Person("Baeldung", "New York");
    assertThat(person, hasProperty("address", equalTo("New York")));
}

2つのPersonオブジェクトが同じ値で構築されているかどうかを確認することもできます。

@Test
public void given2Beans_whenHavingSameValues_thenCorrect() {
    Person person1 = new Person("Baeldung", "New York");
    Person person2 = new Person("Baeldung", "New York");
    assertThat(person1, samePropertyValuesAs(person2));
}

6. コレクションマッチャー

Hamcrest は、コレクションを検査するためのマッチャーを提供します。

コレクションが空かどうかを確認する簡単なチェック:

@Test
public void givenCollection_whenEmpty_thenCorrect() {
    List<String> emptyList = new ArrayList<>();
    assertThat(emptyList, empty());
}

コレクションのサイズを確認するには:

@Test
public void givenAList_whenChecksSize_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers, hasSize(4));
}

これを使用して、配列に必要なサイズがあることを表明することもできます。

@Test
public void givenArray_whenChecksSize_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, arrayWithSize(4));
}

コレクションに、順序に関係なく、指定されたメンバーが含まれているかどうかを確認するには、次の手順に従います。

@Test
public void givenAListAndValues_whenChecksListForGivenValues_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    containsInAnyOrder("beans", "text", "collections", "number"));
}

Collection メンバーが指定された順序であることをさらに主張するには、次のようにします。

@Test
public void givenAListAndValues_whenChecksListForGivenValuesWithOrder_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    contains("collections", "beans", "text", "number"));
}

配列に特定の要素が1つあるかどうかを確認するには、次のようにします。

@Test
public void givenArrayAndValue_whenValueFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, hasItemInArray("text"));
}

同じテストに代替マッチャーを使用することもできます。

@Test
public void givenValueAndArray_whenValueIsOneOfArrayElements_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat("text", isOneOf(hamcrestMatchers));
}

または、次のような別のマッチャーでも同じことができます。

@Test
public void givenValueAndArray_whenValueFoundInArray_thenCorrect() {
    String[] array = new String[] { "collections", "beans", "text",
      "number" };
    assertThat("beans", isIn(array));
}

順序に関係なく、配列に特定の要素が含まれているかどうかを確認することもできます。

@Test
public void givenArrayAndValues_whenValuesFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
      assertThat(hamcrestMatchers,
    arrayContainingInAnyOrder("beans", "collections", "number",
      "text"));
}

配列に指定された要素が含まれているかどうかを確認するには、次のようにします。

@Test
public void givenArrayAndValues_whenValuesFoundInArrayInOrder_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers,
    arrayContaining("collections", "beans", "text", "number"));
}

CollectionMap、の場合、これらのそれぞれの関数で次のマッチャーを使用できます。

指定されたキーが含まれているかどうかを確認するには:

@Test
public void givenMapAndKey_whenKeyFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasKey("blogname"));
}

および指定された値:

@Test
public void givenMapAndValue_whenValueFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasValue("baeldung"));
}

そして最後に、指定されたエントリ(キー、値):

@Test
public void givenMapAndEntry_whenEntryFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasEntry("blogname", "baeldung"));
}

7. 番号マッチャー

Number マッチャーは、Numberクラスの変数に対してアサーションを実行するために使用されます。

greatThan の状態を確認するには:

@Test
public void givenAnInteger_whenGreaterThan0_thenCorrect() {
    assertThat(1, greaterThan(0));
}

greatThanまたはequalToの状態を確認するには:

@Test
public void givenAnInteger_whenGreaterThanOrEqTo5_thenCorrect() {
    assertThat(5, greaterThanOrEqualTo(5));
}

lessThan の状態を確認するには:

@Test
public void givenAnInteger_whenLessThan0_thenCorrect() {
    assertThat(-1, lessThan(0));
}

lessThanまたはequalToの状態を確認するには:

@Test
public void givenAnInteger_whenLessThanOrEqTo5_thenCorrect() {
    assertThat(-1, lessThanOrEqualTo(5));
}

closeTo の状態を確認するには:

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
    assertThat(1.2, closeTo(1, 0.5));
}

最後のマッチャーに細心の注意を払いましょう、 closeTo。 最初の引数であるオペランドは、ターゲットが比較される引数であり、2番目の引数はオペランドからの許容偏差です。これは、ターゲットがオペランド+偏差またはオペランド偏差の場合、テストに合格することを意味します。

8. テキストマッチャー

String のアサーションは、 Hamcrest のテキストマッチャーを使用して、より簡単に、よりすっきりと、より直感的になります。 このセクションではそれらを見ていきます。

String が空かどうかを確認するには:

@Test
public void givenString_whenEmpty_thenCorrect() {
    String str = "";
    assertThat(str, isEmptyString());
}

String が空であるか、 null であるかを確認するには、次のようにします。

@Test
public void givenString_whenEmptyOrNull_thenCorrect() {
    String str = null;
    assertThat(str, isEmptyOrNullString());
}

空白を無視して2つのStringが等しいかどうかを確認するには、次のようにします。

@Test
public void given2Strings_whenEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, equalToIgnoringWhiteSpace(str2));
}

また、特定のStringに特定の順序で1つ以上のサブ文字列が存在するかどうかを確認することもできます。

@Test
public void givenString_whenContainsGivenSubstring_thenCorrect() {
    String str = "calligraphy";
    assertThat(str, stringContainsInOrder(Arrays.asList("call", "graph")));
}

最後に、大文字と小文字に関係なく、2つのStringが等しいかどうかを確認できます。

@Test
 public void given2Strings_whenEqual_thenCorrect() {
    String a = "foo";
    String b = "FOO";
    assertThat(a, equalToIgnoringCase(b));
}

9. コアAPI

Hamcrest コアAPIは、サードパーティのフレームワークプロバイダーによって使用されます。 ただし、単体テストを読みやすくするための優れた構成と、同じように簡単に使用できるコアマッチャーを提供します。

マッチャーのisコンストラクトによる読みやすさ:

@Test
public void given2Strings_whenIsEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, is(equalToIgnoringWhiteSpace(str2)));
}

is は、単純なデータ型で構成されています。

@Test
public void given2Strings_whenIsEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "text";
    assertThat(str1, is(str2));
}

マッチャーのnot構文による否定:

@Test
public void given2Strings_whenIsNotEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " texts ";
    assertThat(str1, not(equalToIgnoringWhiteSpace(str2)));
}

単純なデータ型のnot構造:

@Test
public void given2Strings_whenNotEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "texts";
    assertThat(str1, not(str2));
}

Stringに特定のサブ文字列が含まれているかどうかを確認します。

@Test
public void givenAStrings_whenContainsAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, containsString(str2));
}

Stringが指定されたサブ文字列で始まるかどうかを確認します。

@Test
public void givenAString_whenStartsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, startsWith(str2));
}

Stringが指定されたサブ文字列で終わっているかどうかを確認します。

@Test
public void givenAString_whenEndsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "phy";
    assertThat(str1, endsWith(str2));
}

2つのObjectが同じインスタンスであるかどうかを確認します。

@Test
public void given2Objects_whenSameInstance_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, sameInstance(cat));
}

Objectが特定のクラスのインスタンスであるかどうかを確認します。

@Test
public void givenAnObject_whenInstanceOfGivenClass_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, instanceOf(Cat.class));
}

コレクションのすべてのメンバーが次の条件を満たすかどうかを確認します。

@Test
public void givenList_whenEachElementGreaterThan0_thenCorrect() {
    List<Integer> list = Arrays.asList(1, 2, 3);
    int baseCase = 0;
    assertThat(list, everyItem(greaterThan(baseCase)));
}

Stringnullでないことを確認してください。

@Test
public void givenString_whenNotNull_thenCorrect() {
    String str = "notnull";
    assertThat(str, notNullValue());
}

条件を連鎖させ、論理ORと同様に、ターゲットがいずれかの条件を満たしたときにテストに合格します。

@Test
public void givenString_whenMeetsAnyOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "foo";
    assertThat(str, anyOf(startsWith(start), containsString(end)));
}

条件をまとめると、論理ANDと同様に、ターゲットがすべての条件を満たしている場合にのみテストに合格します。

@Test
public void givenString_whenMeetsAllOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "phy";
    assertThat(str, allOf(startsWith(start), endsWith(end)));
}

10. カスタムマッチャー

TypeSafeMatcher を拡張することで、独自のマッチャーを定義できます。 このセクションでは、ターゲットが正の整数の場合にのみテストに合格できるカスタムマッチャーを作成します。

public class IsPositiveInteger extends TypeSafeMatcher<Integer> {

    public void describeTo(Description description) {
        description.appendText("a positive integer");
    }

    @Factory
    public static Matcher<Integer> isAPositiveInteger() {
        return new IsPositiveInteger();
    }

    @Override
    protected boolean matchesSafely(Integer integer) {
        return integer > 0;
    }

}

ターゲットが実際に正の整数であることを確認するmatchSafelyメソッドと、テストに合格しなかった場合に失敗メッセージを生成するdescribeToメソッドを実装するだけで済みます。

新しいカスタムマッチャーを使用したテストは次のとおりです。

@Test
public void givenInteger_whenAPositiveValue_thenCorrect() {
    int num = 1;
    assertThat(num, isAPositiveInteger());
}

正でない整数を渡したために表示される失敗メッセージは次のとおりです。

java.lang.AssertionError: Expected: a positive integer but: was <-1>

11. 結論

このチュートリアルでは、 Hamcrest API を探索し、それを使用してより優れた、より保守しやすい単体テストを作成する方法を学びました。

これらすべての例とコードスニペットの完全な実装は、私のHamcrestgithubプロジェクトにあります。