JSpecのガイド

JSpecのガイド

1. 概要

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

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

JSpec is another framework that allows us to write fluent assertions closer to the way we write specifications in our natural language、ただし他のフレームワークとは少し異なります。

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

2. Mavenの依存関係

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


    org.javalite
    javalite-common
    1.4.13

最新バージョンについては、Maven Central repositoryを確認してください。

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

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

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

assertEquals(1 + 1, 2);

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

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

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

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

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

この方法でアサーションを記述すると、easier to represent the real specificationsになり、TDDおよびBDDの概念が促進されます。

この例が仕様の自然な記述に非常に近いことを見てください。

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

4. 仕様の構造

The specification statement consists of two parts:は、期待値の作成者と期待値のメソッドです。

4.1. 期待の創造者

静的にインポートされたメソッドa()the()it()$():のいずれかを使用する期待値作成者generates an Expectation object

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

これらのメソッドはすべて基本的に同じです。仕様を表現するさまざまな方法を提供するためだけに存在しています。

唯一の違いは、the it() method is type-safeであり、同じタイプのオブジェクトのみを比較できます。

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

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

4.2. 期待方法

仕様ステートメントの2番目の部分は、期待値メソッドです。これは、shouldEqualshouldContainのようなtells about the required specificationです。

テストが失敗すると、タイプ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()

これらのメソッドを使用してwhether a named boolean property of the object should/shouldn’t return 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クラスに署名“boolean isEmpty()”.を持つメソッドが含まれている必要があります

失敗シナリオ:

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.example.jspec.Dog is not class com.example.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(4) {
    @Override
    public Integer exec() {
        return 2 + 3;
    }
});

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

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

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

失敗シナリオ:

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

cage.release(felixCat);

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

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

6.2. 例外の期待

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

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

expect(new ExceptionExpectation(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. 結論

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

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

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