Lambda Behaveの紹介

1概要

この記事では、https://github.com/RichardWarburton/lambda-behave[Lambda Behave]という新しいJavaベースのテストフレームワークについて説明します。

その名のとおり、このテストフレームワークはJava 8 Lambdasと連携するように設計されています。さらに、この記事では、仕様を調べてそれぞれの例を見ていきます。

含める必要があるMavenの依存関係は次のとおりです。

<dependency>
    <groupId>com.insightfullogic</groupId>
    <artifactId>lambda-behave</artifactId>
    <version>0.4</version>
</dependency>

最新版はhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com.insightfullogic%22%20AND%20a%3A%22lambda-behave%22[here]にあります。

2基本

フレームワークの目標の1つは、優れた読みやすさを実現することです。構文は、ほんの数語ではなく全文を使用してテストケースを記述することを推奨します。

パラメータ化されたテストを利用することができ、テストケースをあらかじめ定義された値に限定したくない場合は、ランダムなパラメータを生成することができます。

3 Lambda Behaveテストの実装

すべての仕様スイートは Suite.describe. で始まります。この時点で、仕様を宣言するための組み込みメソッドがいくつかあります。したがって、 Suite はJUnitテストクラスのようなもので、仕様はJUnitの @ Test というアノテーションが付けられたメソッドのようなものです。

テストを記述するために、 should()を使用します。同様に、期待値lambdaパラメーターに "expect"と名前を付けた場合、 expect.that() によって、テストから期待される結果がわかります。

仕様の前後にデータを設定または削除したい場合は、 it.isSetupWith() および it.isConcludedWith()を使用できます。 it.initiatizesWith() it.completesWith()を使用してください。

Calculator クラスの簡単なテスト仕様の例を見てみましょう。

public class Calculator {

    public int add() {
        return this.x + this.y;
    }

    public int divide(int a, int b) {
        if (b == 0) {
            throw new ArithmeticException();
        }
        return a/b;
    }
}

Suite.describe から始めて、__Calculatorを初期化するコードを追加します。

次に、仕様を記述して add() メソッドをテストします。

{
    Suite.describe("Lambda behave example tests", it -> {
        it.isSetupWith(() -> {
            calculator = new Calculator(1, 2);
        });

        it.should("Add the given numbers", expect -> {
            expect.that(calculator.add()).is(3);
        });
}

ここでは、読みやすくするために、変数に“ it” に“ expect” __という名前を付けました。

これらはラムダパラメータ名なので、これらを任意の名前に置き換えることができます。

should() の最初の引数は平易な英語の使用について説明しています。このテストでは何をチェックするべきですか。 2番目の引数はラムダです。これは、 add() メソッドが3を返す必要があることを示しています。

0で除算するテストケースをもう1つ追加して、例外が発生するかどうかを確認しましょう。

it.should("Throw an exception if divide by 0", expect -> {
    expect.exception(ArithmeticException.class, () -> {
        calculator.divide(1, 0);
    });
});

この場合、例外を期待しているので、 expect.exception() と記述し、その中に、例外をスローするコードを記述します。

テキストの説明はすべての仕様に対して一意でなければならないことに注意してください。

4データ駆動仕様

このフレームワークは仕様レベルでテストのパラメータ化を可能にします。

例を作成するために、 Calculator クラスにメソッドを追加しましょう。

public int add(int a, int b) {
    return a + b;
}

データ駆動型テストを書いてみましょう。

it.uses(2, 3, 5)
  .and(23, 10, 33)
  .toShow("%d + %d = %d", (expect, a, b, c) -> {
    expect.that(calculator.add(a, b)).is(c);
});

uses() メソッドは、入力データを異なる列数で指定するために使用されます。最初の2つの引数は add() 関数のパラメータで、3番目の引数は期待される結果です。これらのパラメータは、テストに示されているように説明にも使用できます。

toShow() は、パラメータを使用してテストを記述するために使用されます。次の出力があります。

0: 2 + 3 = 5 (seed: 42562700892554)(Lambda behave example tests)
1: 23 + 10 = 33 (seed: 42562700892554)(Lambda behave example tests)

5生成された仕様 - プロパティベースのテスト

通常、私たちがユニットテストを書くとき、私たちは私たちのシステムにあてはまるより広い特性に集中したいと思います。

たとえば、 String 逆転関数をテストするときに、特定の String を2回逆転させると、元の String. になることを確認することがあります。

  • プロパティベースのテストでは、特定のテストパラメータをハードコーディングせずに一般的なプロパティに焦点を当てています。

この戦略はデータ駆動型仕様を使用するのと似ていますが、データのテーブルを指定する代わりに、生成するテストケースの数を指定します。

したがって、 String リバースプロパティベースのテストは次のようになります。

it.requires(2)
  .example(Generator.asciiStrings())
  .toShow("Reversing a String twice returns the original String",
    (expect, str) -> {
        String same = new StringBuilder(str)
          .reverse().reverse().toString();
        expect.that(same).isEqualTo(str);
   });

requires() メソッドを使用して必要なテストケースの数を示しました。 example() 句を使用して、必要なオブジェクトの種類とその方法を説明します。

この仕様の出力は次のとおりです。

0: Reversing a String twice returns the original String(ljL+qz2)
  (seed: 42562700892554)(Lambda behave example tests)
1: Reversing a String twice returns the original String(g)
  (seed: 42562700892554)(Lambda behave example tests)

5.1. 決定論的テストケースの生成

自動生成されたテストケースを使用すると、テストの失敗を特定することが非常に困難になります。例えば、** 私たちの機能が1000回に1回失敗すると、たった10個のケースを自動生成する仕様がエラーを観察するために何度も何度も実行されなければならないでしょう。

そのため、以前に失敗したケースも含めて、確定的にテストを再実行する機能が必要です。

Lambda Behaveはこの問題に対処することができます。前のテストケースの出力に示されているように、ランダムなテストケースのセットを生成するために使用されたシードを出力します。そのため、何か失敗した場合は、** 以前に生成したテストケースを再作成するためにシードを使用できます。

テストケースの出力を見て、シードを特定できます。

(seed:42562700892554) 。これで、同じテストのセットをもう一度生成するために、 SourceGenerator を使用できます。

SourceGenerator には、シードのみを引数として取る deterministicNumbers() メソッドが含まれています。

 it.requires(2)
   .withSource(SourceGenerator.deterministicNumbers(42562700892554L))
   .example(Generator.asciiStrings())
   .toShow("Reversing a String twice returns the original String",
     (expect, str) -> {
       String same = new StringBuilder(str).reverse()
         .reverse()
         .toString();
       expect.that(same).isEqualTo(str);
});

このテストを実行すると、以前と同じ出力が得られます。

6. 結論

この記事では、Lambda Behaveと呼ばれる新しい流暢なテスト・フレームワークで、Java 8のラムダ式を使用して単体テストを記述する方法について説明しました。

いつものように、これらの例のためのコードはhttps://github.com/eugenp/tutorials/tree/master/testing-modules/testing[GitHubの上に]を見つけることができます。