1. 概要

JUnitTestNGなどのテストランナーフレームワークは、いくつかの基本的なアサーションメソッド( assertTrue assertNotNull など)を提供します。

次に、 Hamcrest AssertJ Truth などのアサーションフレームワークがあり、通常は「assertThat」で始まる名前の流暢で豊富なアサーションメソッドを提供します。

JSpecは、他のフレームワークとは少し異なりますが、自然言語で仕様を記述する方法に近い流暢なアサーションを記述できるようにする別のフレームワークです。

この記事では、JSpecの使用方法を学習します。 仕様の記述に必要な方法と、テストが失敗した場合に出力されるメッセージを示します。

2. Mavenの依存関係

JSpecを含むjavalite-common依存関係をインポートしましょう。

<dependency>
    <groupId>org.javalite</groupId>
    <artifactId>javalite-common</artifactId>
    <version>1.4.13</version>
</dependency>

最新バージョンについては、MavenCentralリポジトリを確認してください。

3. アサーションスタイルの比較

ルールに基づいてアサートする一般的な方法の代わりに、動作の仕様を記述します。 JUnit、AssertJ、およびJSpecで同等性をアサートするための簡単な例を見てみましょう。

JUnitでは、次のように記述します。

assertEquals(1 + 1, 2);

そして、AssertJでは、次のように記述します。

assertThat(1 + 1).isEqualTo(2);

JSpecで同じテストを作成する方法は次のとおりです。

$(1 + 1).shouldEqual(2);

JSpecは、流暢なアサーションフレームワークと同じスタイルを使用しますが、先頭の assert / assertThat キーワードを省略し、代わりにshouldを使用します。

このようにアサーションを作成すると、実際の仕様を簡単に表現できるようになり、TDDとBDDの概念が促進されます。

この例が私たちの自然な仕様書に非常に近いことを見てください。

String message = "Welcome to JSpec demo";
the(message).shouldNotBe("empty");
the(message).shouldContain("JSpec");

4. 仕様の構造

仕様ステートメントは2つの部分で構成されています:期待値作成者と期待値メソッド。

4.1. 期待クリエーター

期待値作成者は、静的にインポートされた次のメソッドの1つを使用して期待値オブジェクトを生成します: a() the() it() $():

$(1 + 2).shouldEqual(3);
a(1 + 2).shouldEqual(3);
the(1 + 2).shouldEqual(3);
it(1 + 2).shouldEqual(3);

これらの方法はすべて基本的に同じです。これらはすべて、仕様を表現するためのさまざまな方法を提供するためにのみ存在します。

唯一の違いは、 it()メソッドがタイプセーフであり、同じタイプのオブジェクトのみを比較できることです。

it(1 + 2).shouldEqual("3");

it()を使用して異なるタイプのオブジェクトを比較すると、コンパイルエラーが発生します。

4.2. 期待方法

仕様ステートメントの2番目の部分は期待値メソッドであり、 shouldEqual shouldContainのような必要な仕様について通知します。

テストが失敗すると、タイプjavalite.test.jspec.TestExceptionの例外が表現力豊かなメッセージを表示します。 次のセクションで、これらの失敗メッセージの例を示します。

5. 組み込みの期待

JSpecは、いくつかの種類の期待値メソッドを提供します。 テストの失敗時にJSpecが生成する失敗メッセージを示すそれぞれのシナリオを含め、それらを見てみましょう。

5.1. 平等の期待

shouldEqual()、shouldBeEqual()、shouldNotBeEqual()

これらは、 java .lang.Object.equals()メソッドを使用して等しいかどうかをチェックし、2つのオブジェクトが等しい/等しくないことを指定します。

$(1 + 2).shouldEqual(3);

障害シナリオ:

$(1 + 2).shouldEqual(4);

次のメッセージが表示されます。

Test object:java.lang.Integer == <3>
and expected java.lang.Integer == <4>
are not equal, but they should be.

5.2. ブールプロパティの期待

shouldHave()、shouldNotHave()

これらのメソッドを使用して、オブジェクトの名前付きブールプロパティがtrueを返すかどうかを指定します:

Cage cage = new Cage();
cage.put(tomCat, boltDog);
the(cage).shouldHave("animals");

これには、Cageクラスに署名付きのメソッドが含まれている必要があります。

boolean hasAnimals() {...}

障害シナリオ:

the(cage).shouldNotHave("animals");

次のメッセージが表示されます。

Method: hasAnimals should return false, but returned true

shouldBe()、shouldNotBe()

これらを使用して、テスト対象のオブジェクトが何かである必要があるかどうかを指定します。

the(cage).shouldNotBe("empty");

これには、 Cage クラスに、署名「booleanisEmpty()」を持つメソッドが含まれている必要があります。

障害シナリオ:

the(cage).shouldBe("empty");

次のメッセージが表示されます。

Method: isEmpty should return true, but returned false

5.3. タイプ期待

shouldBeType()、shouldBeA()

これらのメソッドを使用して、オブジェクトが特定のタイプである必要があることを指定できます。

cage.put(boltDog);
Animal releasedAnimal = cage.release(boltDog);
the(releasedAnimal).shouldBeA(Dog.class);

障害シナリオ:

the(releasedAnimal).shouldBeA(Cat.class);

次のメッセージが表示されます。

class com.baeldung.jspec.Dog is not class com.baeldung.jspec.Cat

5.4. ヌル可能性の期待

shouldBeNull()、shouldNotBeNull()

これらを使用して、テスト対象のオブジェクトをnullにする/すべきでないことを指定します。

cage.put(boltDog);
Animal releasedAnimal = cage.release(dogY);
the(releasedAnimal).shouldBeNull();

障害シナリオ:

the(releasedAnimal).shouldNotBeNull();

次のメッセージが表示されます。

Object is null, while it is not expected

5.5. 参照期待

shouldBeTheSameAs()、shouldNotBeTheSameAs()

これらのメソッドは、オブジェクトの参照が期待されるものと同じである必要があることを指定するために使用されます。

Dog firstDog = new Dog("Rex");
Dog secondDog = new Dog("Rex");
$(firstDog).shouldEqual(secondDog);
$(firstDog).shouldNotBeTheSameAs(secondDog);

障害シナリオ:

$(firstDog).shouldBeTheSameAs(secondDog);

次のメッセージが表示されます。

references are not the same, but they should be

5.6. コレクションと文字列の内容の期待

shouldContain()、shouldNotContain()これらを使用して、テストされたCollectionまたはMapに特定の要素が含まれるべき/含まれないように指定します。

cage.put(tomCat, felixCat);
the(cage.getAnimals()).shouldContain(tomCat);
the(cage.getAnimals()).shouldNotContain(boltDog);

障害シナリオ:

the(animals).shouldContain(boltDog);

次のメッセージが表示されます。

tested value does not contain expected value: Dog [name=Bolt]

これらのメソッドを使用して、Stringに特定のサブストリングを含めるかどうかを指定することもできます。

$("Welcome to JSpec demo").shouldContain("JSpec");

奇妙に思えるかもしれませんが、この動作を他のオブジェクトタイプに拡張して、 toString()メソッドを使用して比較することができます。

cage.put(tomCat, felixCat);
the(cage).shouldContain(tomCat);
the(cage).shouldNotContain(boltDog);

明確にするために、 CatオブジェクトtomCattoString()メソッドは次のように生成します。

Cat [name=Tom]

これは、 cageオブジェクトのtoString()出力のサブストリングです。

Cage [animals=[Cat [name=Tom], Cat[name=Felix]]]

6. カスタムの期待

組み込みの期待値に加えて、JSpecではカスタムの期待値を記述できます。

6.1. 違いの期待

DifferenceExpectation を記述して、あるコードを実行した場合の戻り値が特定の値と等しくならないように指定できます。

この簡単な例では、演算(2 + 3)で結果(4)が得られないことを確認しています。

expect(new DifferenceExpectation<Integer>(4) {
    @Override
    public Integer exec() {
        return 2 + 3;
    }
});

また、これを使用して、コードを実行すると、変数またはメソッドの状態または値が変更されることを確認できます。

たとえば、2匹の動物を含むケージから動物を解放する場合、サイズは異なる必要があります。

cage.put(tomCat, boltDog);
expect(new DifferenceExpectation<Integer>(cage.size()) {
    @Override
    public Integer exec() {
        cage.release(tomCat);
        return cage.size();
    }
});

障害シナリオ:

ここでは、ケージ内に存在しない動物を解放しようとしています。

cage.release(felixCat);

サイズは変更されず、次のメッセージが表示されます。

Objects: '2' and '2' are equal, but they should not be

6.2. 例外の期待

ExceptionExpectation を記述して、テストされたコードがExceptionをスローするように指定できます。

予期される例外タイプをコンストラクターに渡し、ジェネリック型として提供します。

expect(new ExceptionExpectation<ArithmeticException>(ArithmeticException.class) {
    @Override
    public void exec() throws ArithmeticException {
        System.out.println(1 / 0);
    }
});

失敗シナリオ#1:

System.out.println(1 / 1);

この行は例外を発生させないため、実行すると次のメッセージが生成されます。

Expected exception: class java.lang.ArithmeticException, but instead got nothing

失敗シナリオ#2:

Integer.parseInt("x");

これにより、予想される例外とは異なる例外が発生します。

class java.lang.ArithmeticException,
but instead got: java.lang.NumberFormatException: For input string: "x"

7. 結論

他の流暢なアサーションフレームワークは、コレクションアサーション、例外アサーション、およびJava 8統合のためのより優れた方法を提供しますが、JSpecは、仕様の形式でアサーションを記述するための独自の方法を提供します。

自然言語のようにアサーションを記述できるシンプルなAPIがあり、説明的なテスト失敗メッセージを提供します。

これらすべての例の完全なソースコードは、GitHub–パッケージcom.baeldung.jspecにあります。