2
votes

For my testing, I would like to generate arbitrary random functions of type String => Boolean.

Is it possible to do that using ScalaCheck?

2

2 Answers

5
votes

Yes, just like you'd generate arbitrary values of other types:

import org.scalacheck._

// Int instead of Boolean to better see that it is a function
val arb = implicitly[Arbitrary[String => Int]].arbitrary.sample.get

println(arb("a"))
println(arb("a")) // same
println(arb("b"))

because there is an implicit Cogen[String] and an Arbitrary[Boolean]. Cogen is not documented in the User Guide, but it's equivalent to QuickCheck's CoArbitrary which is explained in https://kseo.github.io/posts/2016-12-14-how-quick-check-generate-random-functions.html and https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html (under "CoArbitrary and Gen (a -> b)").

Is it possible then to generate arbitrary function from a random case class then? For example

case class File(name: Str, size:Long)

It should be enough to define a Cogen[File]. Either manually:

implicit val cogenFile: Cogen[File] = Cogen { 
  (seed: Seed, file: File) => Cogen.perturbPair(seed, (file.name, file.size))
}

Slightly more code, but generalizes to more than 2 fields:

implicit val cogenFile: Cogen[File] = Cogen { (seed: Seed, file: File) => 
  val seed1 = Cogen.perturb(seed, file.name)
  Cogen.perturb(seed1, file.size)
}

Or automatically using scalacheck-shapeless:

implicit val cogenFile: Cogen[File] = MkCogen[File].cogen
2
votes

I don't think, you need to generate anything. If you want a random function, just create a random function:

    val randomFunction: String => Boolean = _ => Random.nextBoolean 

Or if you want the output to be stable (same result for multiple calls of the same function with the same parameter):

   def newRandomFunction: String => Boolean =
     mutable.Map.empty[String, Boolean].getOrElseUpdate(_, Random.nextBoolean)