Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get random data generated for scala case classes with the ability to "change some values" for unit testing?

I'm working with a piece of code that has a broad/deep case class hierarchy. For unit testing, I'd like to have "random data" populated in the classes with the ability to change the data for the fields I care about?

Example:

case class Foo(bar: Bar, name: String, value: Int)
case class Bar(baz: Baz, price: Double)
case class Baz(thing: String)

So something like:

val randomFoo = GenerateRandomData(Foo)
randomFoo.bar.baz = Baz("custom for testing")

I've heard of ScalaCheck and Shapeless and Scalacheck-shapeless and they do provide some sort of random data generation but nothing with customization it seems.

I'm currently using ScalaMock but that builds out null fields and breaks testability for "other" tests. I used something similar in .Net like Auto Fixture and was wondering if there was something similar in Scala.

like image 905
PhD Avatar asked Mar 04 '23 00:03

PhD


2 Answers

I think, you are looking for scalaz lense.

It'll do what you want.

However, I gotta say, that using random data for unit testing seems like a horrible idea. How are you going to debug a failure that happens every now and again?

You should invest some time into setting up a deterministic set of constant test objects, that also resemble the actual production data, and then just use that in your tests.

like image 124
Dima Avatar answered May 02 '23 17:05

Dima


Scalacheck does offer a const generator, that allow to define customized / constant strings:

import org.scalacheck._

  val fooGen: Gen[Foo] =
    for {
      baz <- Gen.const("custom for testing").map(Baz)
      price <- Gen.choose[Double](0, 5000)
      name <- Gen.alphaStr
      value <- Gen.choose(0, 100)
    } yield {
      val bar = Bar(baz, price)
      Foo(bar, name, value)
    }

Here is what we get when we run it:

scala> fooGen.sample
res6: Option[Foo] = Some(
  Foo(
    Bar(Baz("custom for testing"), 1854.3159675078969),
    "EegNcrrQyzuazqrkturrvsqylaauxausrkwtefowpbkptiuoHtdfJjoUImgddhsnjuzpoiVpjAtjzulkMonIrzmfxonBmtZS",
    64
  )
)

Update : As @Dima pointed out, a way to derive random values for all fields is to use [scalacheck-shapeless](https://github.com/alexarchambault/scalacheck-shapeless) and lenses for the customization, here is an example that uses Monocle:

  import org.scalacheck.{Arbitrary, Gen}
  import monocle.Lens
  import org.scalacheck.ScalacheckShapeless._

  implicitly[Arbitrary[Foo]]

  val lensBar = Lens[Foo, Bar](_.bar)(bar => _.copy(bar = bar))
  val lensBaz = Lens[Bar, Baz](_.baz)(baz => _.copy(baz = baz))
  val lensThing = Lens[Baz, String](_.thing)(thing => _.copy(thing = thing))

  val lens = (lensBar composeLens lensBaz composeLens lensThing).set("custom for testing")


  val fooGen: Gen[Foo] = Arbitrary.arbitrary[Foo].map(lens)


  println(fooGen.sample)
  // Display 
  // Some(Foo(Bar(Baz(custom for testing),1.2227226413326224E-91),〗❌䟤䉲㙯癏<,-2147483648))
like image 34
Valy Dia Avatar answered May 02 '23 17:05

Valy Dia