1. 概要

この記事では、プロパティテストの概念と、vavrテストライブラリでの実装について説明します。

プロパティベースのテスト(PBT)を使用すると、準拠する必要のある不変条件に関するプログラムの高レベルの動作を指定できます。

2. 特性試験とは何ですか?

プロパティは、不変条件と入力値ジェネレータの組み合わせです。 生成された値ごとに、不変条件は述語として扱われ、その値に対してtrueまたはfalseが生成されるかどうかがチェックされます。

falseを生成する値が1つあるとすぐに、プロパティが改ざんされたと言われ、チェックが中止されます。 特定の量のサンプルデータの後でプロパティを無効にできない場合、プロパティは満たされていると見なされます。

その振る舞いのおかげで、不必要な作業を行わずに条件が満たされない場合、テストは失敗します。

3. Mavenの依存関係

まず、Mavenの依存関係をvavr-testライブラリに追加する必要があります。

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>jvavr-test</artifactId>
    <version>${vavr.test.version}</version>
</dependency>

<properties>
    <vavr.test.version>2.0.5</vavr.test.version> 
</properties>

4. プロパティベースのテストの作成

文字列のストリームを返す関数について考えてみましょう。 単純なルールに基づいて数値を文字列にマップするのは、0以上の無限のストリームです。 ここでは、パターンマッチングと呼ばれる興味深いVavr機能を使用しています。

private static Predicate<Integer> divisibleByTwo = i -> i % 2 == 0;
private static Predicate<Integer> divisibleByFive = i -> i % 5 == 0;

private Stream<String> stringsSupplier() {
    return Stream.from(0).map(i -> Match(i).of(
      Case($(divisibleByFive.and(divisibleByTwo)), "DividedByTwoAndFiveWithoutRemainder"),
      Case($(divisibleByFive), "DividedByFiveWithoutRemainder"),
      Case($(divisibleByTwo), "DividedByTwoWithoutRemainder"),
      Case($(), "")));
}

このようなメソッドの単体テストを作成すると、エラーが発生しやすくなります。これは、エッジケースを忘れて、基本的にすべての可能なシナリオをカバーできない可能性が高いためです。

幸い、すべてのエッジケースをカバーするプロパティベースのテストを作成できます。 まず、テストの入力となる数値を定義する必要があります。

Arbitrary<Integer> multiplesOf2 = Arbitrary.integer()
  .filter(i -> i > 0)
  .filter(i -> i % 2 == 0 && i % 5 != 0);

入力数は2つの条件を満たす必要があることを指定しました。ゼロより大きい必要があり、余りなしで2で割り切れる必要がありますが、5では割り切れない必要があります。

次に、テストされる関数が指定された引数に対して適切な値を返すかどうかをチェックする条件を定義する必要があります。

CheckedFunction1<Integer, Boolean> mustEquals
  = i -> stringsSupplier().get(i).equals("DividedByTwoWithoutRemainder");

プロパティベースのテストを開始するには、プロパティクラスを使用する必要があります。

CheckResult result = Property
  .def("Every second element must equal to DividedByTwoWithoutRemainder")
  .forAll(multiplesOf2)
  .suchThat(mustEquals)
  .check(10_000, 100);

result.assertIsSatisfied();

2の倍数であるすべての任意の整数について、mustEquals述語が満たされる必要があることを指定しています。 check()メソッドは、生成された入力のサイズと、このテストが実行される回数を取ります。

stringsSupplier()関数が、余りなしで2と5で割り切れるすべての入力番号に対して、DividedByTwoAndFiveWithoutRemainder文字列を返すかどうかを検証する別のテストをすばやく作成できます。

任意のサプライヤーとCheckedFunctionを変更する必要があります。

Arbitrary<Integer> multiplesOf5 = Arbitrary.integer()
  .filter(i -> i > 0)
  .filter(i -> i % 5 == 0 && i % 2 == 0);

CheckedFunction1<Integer, Boolean> mustEquals
  = i -> stringsSupplier().get(i).endsWith("DividedByTwoAndFiveWithoutRemainder");

次に、プロパティベースのテストを1000回の反復で実行できます。

Property.def("Every fifth element must equal to DividedByTwoAndFiveWithoutRemainder")
  .forAll(multiplesOf5)
  .suchThat(mustEquals)
  .check(10_000, 1_000)
  .assertIsSatisfied();

5. 結論

この簡単な記事では、プロパティベースのテストの概念について説明しました。

vavrを使用してテストを作成しました -テスト図書館; 使用しました任意、CheckedFunction、財産を使用してプロパティベースのテストを定義するクラス vavr-test。

これらすべての例とコードスニペットの実装は、GitHubにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。