I would like to make my ScalaCheck property tests in my specs2 test suite deterministic, temporarily, to ease debugging. Right now, different values could be generated each time I re-run the test suite, which makes debugging frustrating, because you don't know if a change in observed behaviour is caused by your code changes, or just by different data being generated.
How can I do this? Is there an official way to set the random seed used by ScalaCheck?
I'm using sbt
to run the test suite.
Bonus question: Is there an official way to print out the random seed used by ScalaCheck, so that you can reproduce even a non-deterministic test run?
From
specs2-scalacheck
version4.6.0
this is now a default behaviour:Given the test file
HelloSpec
:build.sbt
config:project/Dependencies
:When you run the test from the
sbt
console:You get the following output:
To reproduce that specific run (i.e with the same seed)You can take the
seed
from the output and pass it using the command linescalacheck.seed
:And this produces the same output as before.
You can also set the seed programmatically using
setSeed
:Yet another way to provide the
Seed
is pass an implicitParameters
where theseed
is set:Here is the documentation about all those various ways. This blog also talks about this.
If you're using pure ScalaCheck properties, you should be able to use the
Test.Params
class to change thejava.util.Random
instance which is used and provide your own which always return the same set of values:def check(params: Test.Parameters, p: Prop): Test.Result
[updated]
I just published a new specs2-1.12.2-SNAPSHOT where you can use the following syntax to specify your random generator:
For
scalacheck-1.12
this configuration worked:For
scalacheck-1.13
it doesn't work anymore since the rng method is removed. Any thoughts?As a general rule, when testing on non-deterministic inputs you should try to echo or save those inputs somewhere when there's a failure.
If the data is small, you can include it in the label or error message that gets shown to the user; for example, in an xUnit-style test: (since I'm new to Scala syntax)
If the data is large, for example an auto-generated DB, you might either store it in a non-volatile location (eg. /tmp with a timestamped name) or show the seed used to generate it.
The next step is important: take that value, or seed, or whatever, and add it to your deterministic regression tests, so that it gets checked every time from now on.
You say you want to make ScalaCheck deterministic "temporarily" to reproduce this issue; I say you've found a buggy edge-case which is well-suited to becoming a unit test (perhaps after some manual simplification).