scalarandomshapelessrandom-data

How to create a random instance of a case class?


Suppose I've got a few case classes, e.g.:

case class C(c1: Int, c2: Double, c3: Option[String])
case class B(b: Int, cs: Seq[C])
case class A(a: String, bs: Seq[B]) 

Now I would like to generate a few instances of A with random values for tests.

I am looking for a generic way to do that. I can probably do it with runtime reflection but I prefer a compile-time solution.

def randomInstance[A](a: A): A = ???

How can I do it ? Can it be done with shapeless ?


Solution

  • The easiest way for you to do that would be using ScalaCheck. You do so by defining a Gen[A] for your instances:

    import org.scalacheck.Gen
    
    final case class C(c1: Int, c2: Double, c3: Option[String])
    object C {
      val cGen: Gen[C] = for {
        c1 <- Gen.posNum[Int]
        c2 <- Gen.posNum[Double]
        c3 <- Gen.option(Gen.oneOf("foo", "bar", "hello"))
      } yield C(c1, c2, c3)
    }
    

    And you consume it:

    object F {
      def main(args: Array[String]): Unit = {
        val randomC: C = C.cGen.sample.get
      }
    }
    

    On top of that, you can add scalacheck-shapeless which generates the Gen[A] for you, with completely random values (where you have no control over them).

    You may also want to look into random-data-generator (thanks @Gabriele Petronella), which simplifies things even further. From the docs:

    import com.danielasfregola.randomdatagenerator.RandomDataGenerator
    
    object MyApp extends RandomDataGenerator {
    
      case class Example(text: String, n: Int)
    
      val example: Example = random[Example]
      // Example(ਈ䈦㈾钜㔪旅ꪔ墛炝푰⡨䌆ᵅ퍧咪, 73967257)
    }
    

    This is also especially helpful in property based testing.