先日社内勉強会でJavaでのプロパティベーステストライブラリとしてjunit-quickcheckが取り上げられていました。
今週の社内勉強会は「やる夫と学ぶプロパティベーステスト」だった。junit-quickcheckが紹介されてたんだけど確かに日本語情報少ない。ScalaCheck等のほうが日本語情報多い気がするのは言語のユーザ層の違いだろうか。https://t.co/cBPehawPPd
— Naoki Takezoe (@takezoen) 2016年6月11日
参加者から「単一のプロパティではなく複数のプロパティを持ったクラスのインスタンスを生成したい場合はどうすればよいか?」という質問があり、junit-quickcheckではそのクラス用のジェネレータを書く必要があるということだったのですが、Scala用のプロパティベーステストライブラリであるScalaCheckではどうなっているのか試してみました。
まず基本的な型からです。ScalaCheckでも基本的な型のジェネレータは標準で提供されているので以下のような感じでテストを書くことができます。
import org.scalacheck.Properties import org.scalacheck.Prop.forAll object IntSpecification extends Properties("Int") { property("add") = forAll { (a: Int, b: Int) => a + b == b + a } }
独自のジェネレータも簡単に書くことができます。
val smallInt = Gen.choose(0, 100) property("add") = forAll(smallInt, smallInt) { (a: Int, b: Int) => a + b >= a }
さて、ここからが本題なのですが、ScalaCheckではジェネレータを組み合わせてケースクラスを生成するジェネレータを定義することができます。ジェネレータが書きやすかったり組み合わせがしやすかったりするのはScalaCheckの嬉しいところです。
val userGen = for { name <- arbitrary[String] age <- Gen.choose(0, 100) } yield User(name, age) property("add") = forAll(userGen, userGen) { (a: User, b: User) => a.age + b.age >= a.age }
scalacheck-shapelessを使うとケースクラス用のジェネレータをマクロで生成することができます。
implicitly[Arbitrary[User]]
property("add") = forAll { (a: User, b: User) =>
a.age + b.age == b.age + a.age
}
ただ、実際にプロパティベーステストを導入する場合要件に応じたカスタムジェネレータを作ることになると思うのであまり使う機会はないかも…。きちんとドメインモデルを設計してプロパティの型を定義している場合は便利かもしれません。