こういう感じで書けます。
import org.scalatest.prop.TableDrivenPropertyChecks import org.scalatest.funspec.AnyFunSpec // 必要なのは TableDrivenPropertyChecks なので、あとは好きなstyleを選んでよい class FooSpec extends AnyFunSpec with TableDrivenPropertyChecks { describe("foo") { it("bar") { // テーブルを定義する。 // たまに名前かぶるのでTestTableにしている。 import org.scalatest.prop.TableDrivenPropertyChecks.{Table => TestTable} val testTable = TestTable( ("lhs", "rhs", "result", "msg"), // 1要素目はヘッダ // Tupleが1つのテストケースにあたる (0 , 1 , 1 , "zero plus one is one"), (1 , 0 , 1 , "one plus zero is one"), (1 , 1 , 2 , "one plus one is two"), (10 , 20 , 40 , "this should be failed") ) // forAllにテーブルを渡す。第2引数は1つのテストケースをTupleで受け取る関数の形をとる。 forAll(testTable) { (lhs, rhs, result, msg) => // withClueはただの見易くするためのヘルパ。 withClue(s"$msg:") { assert(lhs + rhs == result) } } } } }
ScalaTestはプロパティベーステストの一部としてテーブルを用いたプロパティベーステストを用意してくれている。
上掲のコードでテストに失敗すると、以下のようなメッセージが表示される。
TestFailedException was thrown during property evaluation. Message: this should be failed: 30 was not equal to 40 Location: Foo.scala Occurred at table row 3 (zero based, not counting headings), which had values ( lhs = 10, rhs = 20, result = 40, msg = this should be failed )
何番目のケースが落ちたかも教えてくれて便利。rowはヘッダを除いて0ベースで数える。
テストがコケたときにテストケースを探すのは大変なので、自分はwithClue
を使って補足情報としてmsg
を追加出力している。withClue
は、その範囲でテストが落ちたときのメッセージの頭に特定の文字列を挿入してくれるユーティリティ。なのでエラーメッセージもMessage: this should be failed: 30 was not equal to 40
のようになっている。
Generatorを使ったプロパティベーステストを行う場合にはScalaCheckが必要になるが、テーブルを用いたプロパティベーステストの場合、ScalaCheckは必要ない。