Last active
January 30, 2024 13:25
-
-
Save davidallsopp/f65d73fea8b5e5165fc3 to your computer and use it in GitHub Desktop.
Solutions to the ScalaCheck problem that shrinking failing values may generate invalid values, because the constraints of the generator are not respected. This is for using ScalaCheck from within ScalaTest.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} | |
} |
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!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just a typo fix: should be
Shrink.shrinkAny