第8章の「プロパティベースのテスト」に入ります。
この章は一度作ったAPI設計を、その後の検討で作り直す流れになっていて、演習問題の答えが都度変化していきます。そのため、正解を参照しようとしても用意されている正解は先回りして後半の仕様まで取り込まれているものが用意されていて本書の記載に沿って順番に解いていく時点では単なるコピペができません。一方でここで実装したライブラリは後半のモノイドの章でも利用されるので、動くところまで持っていく必要があります。
もともと、プロパティベースのテストに慣れていないと、新しい概念と実装の両方をこなしていかないといけないので、混乱しがちです。ここはやはり、元となったScalaCheckの使い方を十分に理解して進めていくと良いでしょう。
プロパティベースのテスト
ScalaCheckについて
第8章はプロパティベースのテストを扱いますが、本書でもそのベースになっているのはScalaCheckというライブラリです。
本書にも、プロパティベースのテストに関する概念を習得するために必要なことが十分に書かれていますが、やはり元となったライブラリの理解がある程度進んでおいた方が、演習問題を解きやすくなるでしょう。
scalacheck/UserGuide.md at main · typelevel/scalacheck · GitHub
特にEXERCISE 8.1と、8.2に相当コードをまずはScalaCheckで実装してみましょう。
AirSpecとの連携
ScalaCheckは単体でも使えますが、他のテスティングフレームワークとの組み合わせによる使い方が一般的です。ScalaのメジャーなテスティングフレームワークはどれもScalaCheckとの連携が用意されていますが、初回からお進めしているAirSpecにも連携機能が有ります。
https://wvlet.org/airframe/docs/airspec#property-based-testing-with-scalacheck
PropertyCheck
というtraitをインポートするとAirSpecが用意するforAll
関数が使えるようになります。
import wvlet.airspec.* import wvlet.airspec.spi.PropertyCheck class PropertyBasedTest extends AirSpec with PropertyCheck: test("testAllInt") { forAll{ (i:Int) => i.isValidInt shouldBe true } }
ただし、このforAll
メソッドはScalaCheckが用意したものとは異なり、ScalaCheckのforAll
の戻り値の型はProp
ですが、AirSpecのforAll
はUnit
を返します。つまり、複数のforAllを実行し、すべてのテストがパスすることを確認する際に、Prop
が提供する&&
メソッドは使えません。
ただし、複数のforAll
を実行して結果を得るだけであれば、単純にforAll
を複数並べておけば大丈夫です。どれが一つでも失敗すれば、テスト全体が失敗します。
forAll(Gen.listOfN(0, Gen.choose(0, 100))) { l => l.sum shouldBe 0 } forAll(Gen.listOfN(1, Gen.choose(0, 100))) { i => i.sum shouldBe i(0) }
本書の演習問題ではforAll
はProp
を返すインタフェースになっています。少し混乱しがちですが、演習問題を解く際には気をつけていきましょう。
scalapropsについて
Scala用のプロパティベースのテスティングフレームワークとしてscalapropsというのも有ります。
開発の経緯や、ScalaCheckとの違いなどは、この発表資料にまとまっています。