I'd like to have all my properties always be tested with at least a fixed set of special values in addition to some random values. I'd like to define this in my generator specification, not in every test using that generator type. For instance, if I were generating Ints, I'd like my generator to always generate at l开发者_如何学编程east 0, 1 and -1 for each test case. Is this possible?
The best I've come up with so far is to make a sized generator where the smallest n sizes correspond to my n special cases. This is problematic at least because all possible sizes are not tested when the max number of tests is configured to be lower than the max size parameter.
First of all, there already is a bias in Scalacheck so that 0, 1, -1, Int.MaxValue
and Int.MinValue
are very likely to be choosen in addition to other Int
values. So, if that's your worry, don't worry about it. Likewise, empty strings are likely to be generated.
But, if you want to reproduce this behavior for something else, use Gen.oneOf
or Gen.frequency
, perhaps in conjunction with Gen.choose
. Since oneOf
and frequency
take Gen
as parameter, you can combine special cases with generic generators.
For example:
val myArb: Arbitrary[Int] = Arbitrary(Gen.frequency(
1 -> -1,
1 -> 0,
1 -> 1,
3 -> Arbitrary.arbInt.arbitrary
))
Does pretty much what you asked for, with 50% chance of arbitrary ints (which will come with the bias I spoke of), and 16.6% for each of -1, 0 and 1.
I had the same question today & ended up here so thought I'd add my solution which was to generate Prop
s of my special cases prior to using a Gen
, like so:
import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}
// evaluate fn first with some initial values, then with some generated ones
def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn)
// example of usage
val prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
println(c)
passed
}
The forAllAfter
function here first creates Prop
s using Gen.const
for each value that must be tested then combines them with props created using a generator of subsequent values to test.
If you're using ScalaTest
is that you then need to mix the Checkers
trait into your test to evaluate the resultant Prop
like so:
import org.scalatest.WordSpec
import org.scalatest.prop.Checkers
import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}
class TestExample extends WordSpec with Checkers {
def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn)
val prop: Prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
println(c)
passed
}
"Test example" should {
"Work correctly" in {
check(prop)
}
}
}
精彩评论