-
-
Save davidallsopp/f65d73fea8b5e5165fc3 to your computer and use it in GitHub Desktop.
import org.scalatest._ | |
import prop._ | |
import org.scalacheck.Arbitrary._ | |
import org.scalacheck.Gen | |
/** | |
* Solutions to the ScalaCheck problem that shrinking failing values may generate | |
* invalid values, because the constraints of the generator are not respected. | |
* | |
* See also http://stackoverflow.com/questions/20037900/scalacheck-wont-properly-report-the-failing-case | |
* and http://code.google.com/p/scalatest/issues/detail?id=14 | |
*/ | |
class Shrinking extends PropSpec with PropertyChecks with ShouldMatchers { | |
def odd(i: Int) = i % 2 == 1 | |
// Old solution - use whenever{} clause within the property | |
val odds: Gen[Int] = for { | |
n <- arbitrary[Int] | |
if (odd(n)) | |
} yield n | |
property("fails with big numbers") { | |
forAll(odds) { n => | |
whenever(odd(n)) { | |
if (n % 2 == 0) throw new Exception("Argh") // should never happen | |
if (n > 1000) (n / 2 + n / 2 should be(n)) else n should be(n) | |
} | |
} | |
} | |
// New solution - use suchThat() postcondition on the generator | |
// See https://github.com/rickynils/scalacheck/commit/2d92eb6 | |
val odds2: Gen[Int] = arbitrary[Int].suchThat(odd) | |
property("fails with big numbers v2") { | |
forAll(odds2) { n => | |
if (n % 2 == 0) throw new Exception("Argh") // should never happen | |
if (n > 1000) (n / 2 + n / 2 should be(n)) else n should be(n) | |
} | |
} | |
// Alternative solution - disable shrinking entirely (for Ints) | |
// From http://blog.knutwalker.de/2014/01/fun-with-scalatests-propertychecks.html | |
// In pure ScalaCheck you can use forAllNoShrink() but this is not exposed in ScalaTest | |
{ | |
import org.scalacheck.Shrink | |
implicit val noShrink: Shrink[Int] = Shrink.shrinkAny | |
property("fails with big numbers, shrinking disabled") { | |
forAll(odds) { n => | |
if (n % 2 == 0) throw new Exception("Argh") // should never happen | |
if (n > 1000) (n / 2 + n / 2 should be(n)) else n should be(n) | |
} | |
} | |
} | |
} |
@mcarolan thanks! it's sad we have to do this... like you, i end up here frequently
forAllNoShrink
in ScalaTest would have been soooo much useful. I don't like having to disable the shrink for all the tests in a suite, nor to write a suchThat
that would be 15 lines long ..
Incidentally, org.scalacheck.ShrinkLowPriority.shinkAny
is already marked implicit
, so there's no need to create a definition for it. Simply import Shink.shinkAny
into scope wherever you want to change the behavior of forAll
. It's not the most granular mechanism, but it is simple and effective enough.
import Shink.shinkAny
Just a typo fix: should be Shrink.shrinkAny
Ended up after a long journey. It is not really intuitive that
forAll {
Gen.nonEmptyListOf(...)
} { ... }
will fail because it will create empty lists.
I'm using the nuclear option, as stated above import org.scalacheck.Shrink.shrinkAny
Thank you so much!
:) thanks for this, frequently end up at this gist in the pits of frustration.
there is also the nuclear option of disable shrinking for all the things ever:
whacking this here for the next time I end up reading this!