プロパティテストは何を目指しているのか、スイートポイントは何なのか、どこで使うべきなのか知りたいです。テストしたい関数の例を見てみましょう:
f :: [Integer] -> [Integer]
この関数 はf
、数値のリストを取り、奇数を 2 乗し、偶数を除外します。次のように、関数に関するいくつかのプロパティを述べることができます
- 偶数のリストを指定すると、空のリストを返します。
- 奇数のリストを指定すると、結果リストは入力と同じサイズになります。
- 偶数のリストと奇数のリストがあるとすると、それらを結合し、シャッフルして関数に渡すと、結果の長さは奇数のリストの長さになります。
- 正の奇数のリストを指定すると、同じインデックスにある結果リストの各要素は元のリストよりも大きくなります
- 奇数と偶数のリストを提供し、それらを結合してシャッフルすると、各番号が奇数のリストが得られます
- 等
関数が最も単純なケースで機能することをテストするプロパティはありません。たとえば、f
間違って実装した場合にこれらのプロパティを渡す単純なケースを作成できます。
f = fmap (+2) . filter odd
したがって、単純なケースをカバーしたい場合は、プロパティ仕様でアルゴリズムの基本的な部分を繰り返すか、値ベースのテストを使用する必要があるようです。私が持っている最初のオプションは、アルゴリズムを繰り返すのに役立つかもしれません。たとえば、速度のためにアルゴリズムの実装を変更する予定がある場合は、アルゴリズムを改善する予定です。このようにして、再度テストするために使用できる参照実装ができました。
いくつかの些細なケースでアルゴリズムが失敗しないことを確認したい場合、および仕様でアルゴリズムを繰り返したくない場合は、単体テストが必要なようです。たとえば、これらのチェックを記述します。
f ([2,5]) == [25]
f (-8,-3,11,1) == [9,121,1]
今では、アルゴリズムに自信を持っています。
私の質問は、プロパティ ベースのテストは単体テストに取って代わるものですか、それとも補完的なものですか? プロパティをどのように記述するかという一般的な考え方はありますか?それらは便利ですか、それとも関数のロジックの理解に完全に依存していますか? つまり、何らかの方法でプロパティを記述することは特に有益であると言えますか?
また、プロパティがアルゴリズムのすべての部分をテストするように努力する必要がありますか? アルゴリズムから 2 乗を除外して、別の場所でテストし、プロパティでフィルタリング部分だけをテストして、それが適切にカバーされているかどうかをテストすることができます。
f :: (Integer -> Integer) -> [Integer] -> [Integer]
f g = fmap g . filter odd
そして、単体テストを使用して合格し、他Prelude.id
の場所をテストできます。g