Combining ScalaCheck generators
ScalaCheck library comes with a wide variety of built-in generators, but let me show you how to combine them to make the library even more powerful.
Map on generator
Generator comes with a method map
which allows you to transform obtained data. Let's create a generator which generates only positive, even integers:
val evenNumberGenerator: Gen[Int] = Gen.posNum[Int].map(_ * 2)
`
In this case map accepts a function Int => U
, since the generator on which the map is called returns Int
. You can chain maps even further:
val oddNumberGenerator: Gen[Int] = evenNumberGenerator.map(_ + 1)
FlatMap on generator
Let's suppose that you wold like to generate Either[String, Int]
. We can use flatMap
to handle this case. We will create Right
if generated number is even and left if it's odd:
val leftOrRightFiftyFifty: Gen[Either[String, Int]] = Gen.posNum[Int].flatMap{ i =>
if(i % 2 == 0) Gen.posNum[Int].map(Right[String, Int])
else Gen.alphaLowerStr.map(Left[String, Int])
}
FlatMap called on generator accepts a function T => Gen[U]
. This allows you to combine generators further and further, leading to the generator which exactly suits your needs.
Frequency generator
FlatMap is a very powerful tool, but requires a lot of boilerplate. What would happen if we wanted to obtain right 40% of time? What if we would have more and more possibilities? There is a nice built-in solution for this case. Take a look at frequency
combinator:
val leftOrRight: Gen[Either[String, Int]] = Gen.frequency(
4 -> Gen.posNum[Int].map(Right[String, Int]),
6 -> Gen.alphaLowerStr.map(Left[String, Int])
)
Options here and options there
Option generators are well represented in ScalaCheck. If you want to always generate Some[T]
you can use Gen.some
like this:
val someOfIntGenerator: Gen[Option[Int]] = Gen.some(Gen.posNum[Int])
In case you would like to have Some
and None
you can use Gen.option
, to have both of the possibilities. It's used in exactly the same way as Gen.some
:
val optionalIntegerGenerator: Gen[Option[Int]] = Gen.option(Gen.posNum[Int])
What is interesting Gen.option
is implemented using Gen.frequency
. Check it out here.
Exclusive ranges
There might be a possibility that you want to generate a number from non overlapping ranges. Again you can use a nice built-in generator - Gen.oneOf
. In this example we will generate numbers greater than 10 and smaller than 20 or bigger than 40 and smaller than 50. It's as simple as this:
val oneOfRanges: Gen[Int] = Gen.oneOf(
Gen.chooseNum(10, 20),
Gen.chooseNum(40, 50)
)
Conclusion
Hopefully i managed to show you that ScalaCheck generators are easily combined, leading to code which can fit your exact needs.
Keep in mind that the examples I shown above are rather simple, but I hope you can already see usages which will fit your domain requirements.