Chapter 7

Running ScalaCheck

Throughout this book, almost every property or property collection has been tested by running the check method without any parameters. This is probably not the way you would verify properties in a real life project. For one thing, you might need to tweak the testing parameters to fit your needs. And you would definitely want to run the verification automatically as part of your build process, not programmatically by executing check methods. In this chapter, I will show how to do all of this in practice.

I will first show the various ways of retrieving ScalaCheck, then describe the steps that ScalaCheck goes through when testing a property and also introduce the test parameters that are available for you to adjust. Finally, I'll talk about different ways of running ScalaCheck tests as part of a build process: with ScalaTest, with sbt, from the command line or programmatically.

7.1 Retrieving ScalaCheck

The latest ScalaCheck release and instructions for retrieving and running it are available on the ScalaCheck web site, http://www.scalacheck.org. This section will describe the standard ways of retrieving ScalaCheck at the time this book was written.

Standalone ScalaCheck JAR file

Other than the Scala runtime, ScalaCheck has no dependencies. Therefore, it is easy to drop the single ScalaCheck JAR file into your project. The simplest way to get the JAR file is by visiting http://www.scalacheck.org. You will find that for each version of ScalaCheck there are releases for each major version of Scala. This is because the different major versions of Scala often are binary incompatible; you cannot use a library compiled with Scala 2.9 in a project that you build with Scala 2.10. Look for a ScalaCheck release that has an artifact id ending with a version that matches the Scala compiler you use. When new major versions of Scala are released, new ScalaCheck builds are made available.

Using sbt

The sbt build tool can run ScalaCheck tests automatically, and also handles dependencies. To use ScalaCheck with sbt, include the correct dependency in your build.sbt file. Listing 7.1 is an example of a minimal sbt project that uses ScalaCheck 1.11.0:

    name := "MySbtProject"
  
  version := "1.0"
  scalaVersion := "2.10.1"
  libraryDependencies +=     "org.scalacheck" %% "scalacheck" % "1.11.0" % "test"
Listing 7.1 - A minimal sbt build file that uses ScalaCheck for testing.

If you copy Listing 7.1 into a build.sbt file, be sure to include the blank lines. Trigger tests with the command sbt test. When sbt runs tests, it will automatically scan the src/test/scala directory in your project for classes that implement org.scalacheck.Properties that was described in Chapter 3 and check all properties they contain.

In the end of this chapter I'll go into more details on how to use sbt and ScalaCheck together.

Using Maven

If you use Maven, you can get it to fetch ScalaCheck automatically by including the following dependency:

<repositories> <repository> <id>oss.sonatype.org</id> <name>releases</name> <url> http://oss.sonatype.org/content/repositories/releases </url> </repository> </repositories> <dependency> <groupId>org.scalacheck</groupId> <artifactId>scalacheck_2.10</artifactId> <version>1.11.0</version> </dependency>

Take care to pick an artifact id that matches your version of the Scala compiler.

7.2 ScalaCheck's testing workflow

For ScalaCheck, testing a property means trying to falsify it. If a property fails, you can be sure ScalaCheck has found a set of input values that make the property false. If the property passes, you can only be sure that ScalaCheck has tried its best (or, tried as hard as you've told it to try) to find such input values, but not succeeded. You can't be sure that it is impossible to make the property false. That is the nature of software testing, really. You can only prove the existence of bugs, not the absence of them. With a much more rigorous specification language (and an advanced test runner on top of it) you can prove correctness for at least some kinds of software, but that is not the road ScalaCheck takes.

For each property, the routine ScalaCheck follows is roughly this:

  1. Approximate how many times the property needs to be evaluated. This depends on how many tests you have told ScalaCheck each property must pass before the whole property is passed, and also on how many tests are allowed to be discarded. The workload is then divided by the number of worker threads specified.
  2. Launch all worker threads; each begins to iteratively evaluate the property in parallel.
  3. On each iteration, a generator size is calculated. The size is used by some generators to decide how they should produce data. You can see more about this in Chapter 6. The calculated size depends on the testing parameters and on the current iteration index.
  4. Evaluate the property with the current iteration's generator size. The result of the evaluation is recorded. If the evaluation fails, all workers are interrupted, and the property is reported as failed. If the evaluation passes, or if it neither fails nor succeeds (a discarded result), the workers keep evaluating the property.
  5. The workers keep working either until the required number of tests have passed, or until too many tests have been discarded. The property result is then reported back.

7.3 Test parameters

ScalaCheck uses a number of different parameters when testing a property. As a user of ScalaCheck, you can change these parameters according to your needs. In this section, I will describe what the various parameters mean and why you could want to adjust them, but I will not go much into detail on the practical issues around how to set the parameters when running tests. Such details are instead left for the last section of this chapter that describes the various ways you can integrate ScalaCheck testing into a build process.

Minimum number of successful tests

The minSuccessfulTests parameter tells ScalaCheck how many times a property must evaluate to true before it should be considered passed. The default value is 100. This means that ScalaCheck will test each property with random input arguments at least 100 times, as long as it doesn't find a set of arguments that falsify the property. If it does, it will abort the property's testing immediately and report the failure.

You might want to increase this number if you have a large range of possible input parameters to your property, with corner cases that are difficult to find. You can also increase it if you want to be more certain that ScalaCheck hasn't missed any failing cases. Increasing this parameter will, of course, increase the time it takes to verify a property. If you can afford it, you might as well do it. If you have integrated ScalaCheck into your build process, you could for example set this parameter higher on the nightly builds and lower on the development builds.

Maximum ratio between discarded and successful tests

Apart from setting the minimum number of successful evaluations, it is also possible to tell ScalaCheck how many times it should retry if a property evaluates to neither true or false. For example, if a property has a precondition, ScalaCheck might generate arguments that don't fulfill that precondition. In that case, ScalaCheck discards the result of the property evaluation since it doesn't disclose the property's validity. ScalaCheck might discard several tests for every input argument that satisfies a tricky precondition.

The parameter maxDiscardedRatio determines how hard ScalaCheck will try before giving up on a property, by specifying the maximum allowed ratio between discarded and successful tests. The default value of this parameter is 5: if the minimum number of successful tests is 100, then 500 discarded attempts are allowed before property evaluation is aborted. Notice that ScalaCheck will always try at least as many evaluations as the specified minimum number of successful tests, even if the maximum discarded ratio is exceeded at some point during the evaluation iteration. It means, that by default ScalaCheck will do 100 property evaluations no matter what maxDiscardedRatio is set to, and no matter how many tests are discarded.

When ScalaCheck fails to prove or falsify a property, you may choose to raise the allowed maximum ratio between discarded and successful tests. This can happen with a non-trivial precondition, or with generators that have too narrow a filter (added with the Gen.suchThat method, discussed in Chapter 6). In both cases, it is more reliable to use explicit generators, as described in Chapter 3. Adjust maxDiscardedRatio only when the work of implementing explicit generators outweighs the time spent running extra property checks.

Minimum and maximum data size

As mentioned earlier, ScalaCheck can control the size of the generated test data by providing the generator with a size parameter, a hint about how large the generated value should be. This size is bounded by the two parameters minSize and maxSize. When testing a property, ScalaCheck starts with the value of the minimum size parameter for the generators, then increases it in a linear fashion up to the value of the maximum size parameter. By default, the lower bound is zero and the upper bound is 100. It is completely up to the generator implementation how to interpret the size value. A generator can (and many do) completely ignore it. The size makes most sense for generators that produce some kind of collection. Chapter 6 gives examples on how the size can be used in a generator implementation.

Random number generator

The parameter rng specifies which random number generator ScalaCheck should use when generating test data. The parameter should be an instance of scala.util.Random or some subclass of it. By default, it is simply an instance of the standard scala.util.Random class. You won't need to change this parameter unless you require exact control over the way random numbers are generated.

Number of worker threads

ScalaCheck can use several threads in parallel when checking a property. The thread count is controlled by the workers parameter. This allows you to take advantage of multiple processors or processor cores and check a property more quickly than if you did it in a single thread. See the following example, which was executed on a computer with a dual core processor. First we define a property and two different sets of testing parameters:

  import org.scalacheck.{Prop,Test}
  
val p = Prop.forAll { xs: List[String] =>   val s = if(xs.isEmpty) "" else xs.reduce(_+_)   xs.forall(s.contains) }
val oneWorker =   Test.Parameters.default.withMinSuccessfulTests(5000)
val twoWorkers = Test.Parameters.default.   withMinSuccessfulTests(5000).   withWorkers(2)

The number of tests are changed from the default 100 to 5000 to make the difference in running time more obvious. We can now use the Test.check method to check the property. When the property has been checked, it will return an instance of org.scalacheck.Test.Result. The Result class contains a field time that tells us how long time (in milliseconds) it took to test the property:

  scala> Test.check(oneWorker, p).time
   res0: Long = 4100
  
scala> Test.check(oneWorker, p).time  res1: Long = 3982
scala> Test.check(twoWorkers, p).time  res2: Long = 2565
scala> Test.check(twoWorkers, p).time  res3: Long = 2577

When testing properties concurrently like this, make sure that your properties don't have any side effects that could cause race conditions or deadlocks. Also, if you use a custom test callback handler as described below, be aware that the callbacks might come from different threads. By default, the workers parameter is set to 1, which means ScalaCheck will not run property evaluations in parallel.

The Result class is discussed further in a later section of this chapter.

Test execution callback

The testCallback parameter specifies an execution callback handler of the type org.scalacheck.Test.TestCallback. This object will receive callbacks during the test execution. The parameter can be useful if you want to integrate ScalaCheck with another test runner, or if you need to tweak the verbosity level of ScalaCheck's default test runner. In the next section you'll see an example of how to make use of the callback.

7.4 Integrating ScalaCheck

You can incorporate ScalaCheck into your build process in several ways. This section will describe using ScalaCheck via ScalaTest, specs2, and sbt, as well as more direct approaches.

Using ScalaCheck with ScalaTest

ScalaTest provides two ways in its org.scalatest.prop package to incorporate ScalaCheck property checks into your test suites: Checkers and PropertyChecks. Checkers allows you to use native ScalaCheck syntax; PropertyChecks provides syntax more consistent with the rest of ScalaTest.

To show the difference, we'll test this interleave method, first shown in Section 5.1:

  object Interleaver {
    def interleave[T](l1: List[T], l2: List[T]): List[T] = {
      if(l1.isEmpty) l2
      else if(l2.isEmpty) l1
      else l1.head :: l2.head :: interleave(l2.tail, l1.tail)
    }
  }

Here's how you'd test the interleave example with Checkers:

  import Interleaver._
  import org.scalatest._
  import prop._
  import org.scalacheck.Prop.{AnyOperators, forAll, all}
  
class ExampleSpec extends PropSpec with Checkers {
  property("the interleave method must interleave lists") {     check(       forAll { (l1: List[Int], l2: List[Int]) =>         val res = interleave(l1,l2)         val is = (0 to Math.min(l1.length,             l2.length)-1).toList         all(           "length" |: l1.length+l2.length =? res.length,           "zip l1" |: l1 =? is.map(i => res(2*i)) ++               res.drop(2*l2.length),           "zip l2" |: l2 =? is.map(i => res(2*i+1)) ++               res.drop(2*l1.length)         ) :| ("res: "+res)       }     )   } }

If you execute this ExampleSpec, you'll see the information provided by ScalaCheck about the error:

  Run starting. Expected test count is: 1
  ExampleSpec:
  - the interleave method must interleave lists *** FAILED ***
    GeneratorDrivenPropertyCheckFailedException was thrown
       during property evaluation.
     (c.scala:28)
      Falsified after 3 successful property evaluations.
      Location: (c.scala:28)
      Occurred when passed generated values (
        arg0 = List(0, 0), // 2 shrinks
        arg1 = List(0, 1) // 29 shrinks
      )
      Labels of failing property:
        Expected List("0", "0") but got List("0", "1")
        zip l1
        res: List(0, 0, 1, 0)
  Run completed in 241 milliseconds.
  Total number of tests run: 1
  Suites: completed 1, aborted 0
  Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
  *** 1 TEST FAILED ***

Checkers therefore allows you to construct properties the same was as ScalaCheck, and obtain output consistent with running ScalaCheck directly. PropertyChecks, by contrast, allows a more native-ScalaTest style in which assertions and matcher expressions can be used instead of boolean expressions with labels. Here's how the previous example would look if you used the PropertyChecks trait instead of Checkers:

  import Interleaver._
  import org.scalatest._
  import prop._
  import Matchers._
  
class ExampleSpec extends PropSpec with PropertyChecks {
  property("the interleave method must interleave lists") {     forAll { (l1: List[Int], l2: List[Int]) =>       val res = interleave(l1,l2)       val is = (0 to Math.min(l1.length,           l2.length)-1).toList       l1.length + l2.length shouldBe res.length       l1 shouldBe is.map(i => res(2*i)) ++           res.drop(2*l2.length)       l2 shouldBe is.map(i => res(2*i+1)) ++           res.drop(2*l1.length)     }   } }

Note that the forAll method used in this example is from ScalaTest, not ScalaCheck. ScalaTest's forAll can be optionally configured by specifying arguments before the property function. You can pass custom generators, argument names for better error messages, and even ScalaCheck configuration properties like minSuccessful. For examples, look to the ScalaTest documentation.[1] Running this version of ExampleSpec, you'll see:

  Run starting. Expected test count is: 1
  ExampleSpec:
  - the interleave method must interleave lists *** FAILED ***
    TestFailedException was thrown during property evaluation.
      Message: List(0, 0) did not equal List(0, -1)
      Location: (pc.scala:22)
      Occurred when passed generated values (
        arg0 = List(0, 0), // 2 shrinks
        arg1 = List(0, -1) // 3 shrinks
      )
  Run completed in 463 milliseconds.
  Total number of tests run: 1
  Suites: completed 1, aborted 0
  Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
  *** 1 TEST FAILED ***

The main difference compared with Checkers is that you need not use ScalaCheck labels with PropertyChecks, because the assertions (in this case, matcher expressions) will fail with an exception that contains an error message and a stack depth that points to the failing line of code. In an IDE, for example, you can just click and hop to the line of code that starts with l1 shouldBe..., which is the assertion that causes the property check to fail.

Note that although PropSpec was used in these examples, Checkers and PropertyChecks can be used with any style (FunSuite, WordSpec, etc.) in ScalaTest. PropSpec looks similar to ScalaCheck's Properties, but allows you to access all of ScalaTest's other features in addition to letting you define properties.

Using ScalaCheck with specs2

You can also use ScalaCheck with specs2. Here's an example that shows ScalaCheck being used with specs2's acceptance specification style:

  import Interleaver._
  import org.specs2._
  import org.scalacheck.Prop.{AnyOperators, forAll, all}
  
class ExampleSpec extends Specification with ScalaCheck {
  def is = s2"""     the interleave method must interleave lists $e1   """
  def e1 = {     forAll { (l1: List[Int], l2: List[Int]) =>       val res = interleave(l1,l2)       val is = (0 to Math.min(l1.length,         l2.length)-1).toList       all(         "length" |: l1.length+l2.length =? res.length,         "zip l1" |: l1 =? is.map(i => res(2*i)) ++             res.drop(2*l2.length),         "zip l2" |: l2 =? is.map(i => res(2*i+1)) ++             res.drop(2*l1.length)       ) :| ("res: "+res)     }   } }

If you run this ExampleSpec, you'll see output like:

  ExampleSpec
      x the interleave method must interleave lists
   A counter-example is [List(00), List(01)]
       (after 4 tries -
       shrinked ('List(-110685354854)' ->
       'List(00)','List(1026517985, -1621352957,
           1638430452)' -> 'List(01)'))
   Expected List("0""0") but got List("0""1"),
       zip l1, res: List(0010)
  
Total for specification ExampleSpec Finished in 24 ms 1 example, 1 failure, 0 error

As demonstrated here, one of the test types supported by specs2 is a ScalaCheck property. You can simply place a ScalaCheck property where a test is required.

Using ScalaCheck with sbt

Sbt is a popular build tool that handles compilation, testing, packaging and publishing of Scala projects. Sbt has great support for ScalaCheck, and it is the recommended way to run ScalaCheck tests if you want to get started quickly with minimal effort.

Listing 7.1 showed how a minimal sbt project file (build.sbt) can look like. We can set up a simple project with that build file by creating the following directory structure:

  build.sbt
  src/main/scala/MyClass.scala
  src/test/scala/MySpec.scala

MyClass.scala and MySpec.scala look like this:

  case class MyClass(s1: String, s2: String) {
    override def toString = "first = "+s1+", second="+s2
  }
  
object MySpec extends org.scalacheck.Properties("MySpec") {   import org.scalacheck.{GenPropArbitrary}   implicit val arbMyClass = Arbitrary(Gen.resultOf(MyClass))   property("MyClass.toString") =     Prop.forAll { mc: MyClass =>       mc.toString.contains(mc.s1) &&         mc.toString.contains(mc.s2)     } }

If you have sbt installed you can now run the tests for our little project:

  $ sbt test
  [info] Set current project to MySbtProject (in build ...
  [info] Compiling 1 Scala source to /...
  [info] + MySpec.MyClass.toString: OK, passed 100 tests.
  [info] Passed: : Total 1Failed 0Errors 0Passed 1,
    Skipped 0
  [success] Total time: 5 s, completed Sep 102013
    7:55:31 PM

Sbt automatically picks up all Properties instances it finds under the src/test/scala and evaluates the properties.

Setting test parameters

You can set all ScalaCheck's various testing parameters when running property checks through sbt. Use the same option names as for the command line interface (see listing 7.2), and make an addition to your build.sbt file like this:

  testOptions in Test +=
    Tests.Argument(
      TestFrameworks.ScalaCheck,
      "-maxDiscardRatio""10",
      "-minSuccessfulTests""1000"
    )

We can rerun sbt with the updated build file and see that the property is evaluated 1000 times instead of 100:

  $ sbt test
  [info] Set current project to MySbtProject (in build ...
  [info] + MySpec.MyClass.toString: OK, passed 1000 tests.
  [info] Passed: : Total 1Failed 0Errors 0Passed 1,
    Skipped 0
  [success] Total time: 1 s, completed Sep 112013
    8:14:02 AM

For further information on how to use sbt, look at its documentation.

Using ScalaCheck from the command line

Every instance of Prop and Properties carries a built-in main method complete with a command line option parser, which makes it easy to test ScalaCheck properties from the command line. This is one way to integrate ScalaCheck into custom build processes.

Below, a basic property collection that tests some of the List class's methods is defined.

  import org.scalacheck.Properties
  import org.scalacheck.Prop.forAll
  
object ListSpec extends Properties("ListSpec") {   property("append") =     forAll { (xs: List[Int], n: Int) =>       (xs :+ n).last == n     }
  property("insert") =     forAll { (xs: List[Int], n: Int) =>       (n +: xs).head == n     } }

We can compile this property collection and then run it as an ordinary Java or Scala application:

  $ scalac -cp scalacheck.jar ListSpec.scala
  $ scala -cp scalacheck.jar:. ListSpec
  + ListSpec.append: OK, passed 100 tests.
  + ListSpec.insert: OK, passed 100 tests.
Setting test parameters

We can set test parameters from the command line. If you give the application an unknown argument, for example --help, the available options will be printed as in listing 7.2.

    $ scala -cp scalacheck.jar:. ListSpec --help
     Available options:
       -minSize, -n:
          Minimum data generation size
       -maxDiscardRatio, -r:
          The maximum ratio between discarded and succeeded
          tests allowed before ScalaCheck stops testing a
          property. At least minSuccessfulTests will always
          be tested, though.
       -verbosity, -v:
          Verbosity level
       -maxSize, -x:
          Maximum data generation size
       -workers, -w:
          Number of threads to execute in parallel
          for testing
       -minSuccessfulTests, -s:
          Number of tests that must succeed in order to
          pass a property
Listing 7.2 - ScalaCheck's command line options.

With the exception of verbosity, these options correspond to the parameters that we described earlier in this chapter. The verbosity parameter controls how detailed the test result output on the console should be. You can specify an integer value, where 0 corresponds to the lowest verbosity and 1 is the default verbosity level. Setting verbosity to 2 makes ScalaCheck output the time it took to evaluate each property:

  $ scala -cp scalacheck.jar:. ListSpec -v 2 -s 5000
   + ListSpec.append: OK, passed 5000 tests.
   Elapsed time: 0.374 sec
   + ListSpec.insert: OK, passed 5000 tests.
   Elapsed time: 0.124 sec

When running a property or property collection as an application from the command line, its exit value will equal the number of failed properties.

Using ScalaCheck programmatically

As a normal user, you will probably not need to run ScalaCheck tests programmatically, but there are situations when it can be useful. For example, if you're integrating ScalaCheck in some custom build process, or implementing support for it in a test runner. Or, you simply want to explore the ScalaCheck API or debug your properties. In such cases, the Scala interpreter is convenient for quickly trying out ScalaCheck properties. You can define single properties or property collections on the fly and evaluate them in the interpreter, as shown below (and numerous times throughout this book). In the example below we use the org.scalacheck.Test.check method to evaluate properties instead of using the Prop.check convenience method. By default, the Test.check method returns the results as an instance of class org.scalacheck.Test.Result and does not print anything to the console.

The interface of the Result class looks like this:

  case class Result(
    status: Status,
    succeeded: Int,
    discarded: Int,
    freqMap: FreqMap[Set[Any]],
    time: Long
  )

The Result.status field reports the overall status of the property test. The Result.succeeded and Result.discarded fields indicate how many evaluations have passed or been discarded, respectively. Result.freqMap contains statistics that can be collected by using the special property combinators Prop.collect and Prop.classify. Result.time states how long time the total property test took, in milliseconds.

To get a Result object, you need to use the Test.check method. This method takes two arguments: an instance of Test.Parameters that contains the various testing parameters described earlier, and the property that should be checked.

  import org.scalacheck.Prop.forAll
  
val singleProp = forAll { (xs: List[Int], n: Int)  =>   val len = xs.take(n).length   if (n <= 0) len == 0   else if (n > xs.length) len == xs.length   else len == n }
  scala> import org.scalacheck.Test.{check, Parameters}
  
scala> check(Parameters.default, singleProp) res0: org.scalacheck.Test.Result =   Result(Passed,100,0,Map(),9)

There is also a Test.checkProperties method that can be used to test property collections. It returns a list of pairs of property names and their corresponding results:

  import org.scalacheck.Properties
  
object SomeProps extends Properties("SomeProps") {   property("add") = forAll { (n:Int, m:Int) => n+m == m+n }   property("take") = singleProp }
  scala> import org.scalacheck.Test.checkProperties
  
scala> Test.checkProperties(Parameters.default, SomeProps) res1: Seq[(String, org.scalacheck.Test.Result)] =   ListBuffer((SomeProps.add,Result(Passed,100,0,Map(),4)),     (SomeProps.take,Result(Passed,100,0,Map(),4)))
Setting test parameters

To change any parameters mentioned in the previous section when using the check method, create a new instance of the Test.Parameters class and give it to the check method. You can base your parameters instance on Test.Parameters.Default and change the values you are interested in. This is demonstrated below:

  import org.scalacheck.Prop.forAll
  import org.scalacheck.Test.{check, Parameters}
  
val p = forAll { (s1: String, s2: String) =>   val s3 = s1+s2   s3.contains(s1) && s3.contains(s2) }
val myParams = new Parameters.Default {   override val minSuccessfulTests = 400 }
check(myParams, p)

There are also convenient withX methods on the Test.Parameters type that let you create new Parameters instances by changing individual parameters. You can then use Test.Parameters.default which is an instance of Test.Parameters.Default to create a new set of parameters:

  val myParams =
    Parameters.default.withMinSuccessfulTests(400)

You can also use a variant of Test.check or Prop.check to directly set a parameter:

  p.check(_.withMinSuccessfulTests(400))
  
Test.check(p) { _.   withMinSuccessfulTests(400).   withWorkers(4) }

The check-methods shown above take a function from Parameters to Parameters and then applies Test.Parameters.default to that function to get the Parameters instance that will be used for the check.

The definition of trait org.scalacheck.Test.Parameters.Default is below. Here you can see the exact names, and the default values, of its parameters:

  trait Default extends Parameters {
    val minSuccessfulTests: Int = 100
    val minSize: Int = 0
    val maxSize: Int = Gen.Params().size
    val rng: scala.util.Random = Gen.Params().rng
    val workers: Int = 1
    val testCallback: TestCallback = new TestCallback {}
    val maxDiscardRatio: Float = 5
    val customClassLoader: Option[ClassLoader] = None
  }
Using a test callback

The TestCallback class looks like this:

  trait TestCallback {
    /** Called each time a property is evaluated */
    def onPropEval(name: String, threadIdx: Int,
      succeeded: Int, discarded: Int): Unit = ()
  
  /** Called whenever a property has finished testing */   def onTestResult(name: String, result:     Result): Unit = () }

As you can see, by default the callback handler does nothing. However, when the Prop.check method is used to test a property, the callback handler parameter is automatically set to a handler that prints out the test results on the console.

You may wish to set the callback handler yourself if you need to integrate ScalaCheck into your build process, or if you want to adjust the way test results are reported.

The example below defines a test callback that prints a line to the console for each evaluation that is performed:

  import org.scalacheck.Test.{TestCallbackParameters}
  import org.scalacheck.Prop.{forAll, BooleanOperators}
  
val myTestCallback = new TestCallback {   override def onPropEval(     name: String, threadIdx: Int,     succeeded: Int, discarded: Int   ): Unit = {     Console.printf("[%d] %s: s=%d d=%d ", threadIdx,       name, succeeded, discarded)   } }
val myParams = Parameters.default { _.   withWorkers(3).   withTestCallback(myTestCallback) }
val myProp = forAll { n: Int =>   (n > 0) ==> (math.abs(n) == n) }

Running the property check with myParams produces a lot of output:

  scala> org.scalacheck.Test.check(myParams, myProp)
  [2] : s=1 d=0
  [0] : s=1 d=0
  [1] : s=0 d=1
  [0] : s=1 d=1
  ...
  [2] : s=33 d=40
  [2] : s=34 d=40
  res0: org.scalacheck.Test.Result =
    Result(Passed,102,107,Map(),25)

7.5 Conclusion

One of the goals of ScalaCheck is to be a relatively small and self-contained tool. This makes it easy to use it as a core part of a testing workflow. It also makes it simple for other test frameworks to include support for ScalaCheck, something that has been shown in this chapter. You're encouraged to use any testing framework that fits your workflow (or build your own), and let ScalaCheck be one of the tools in your toolbox. Hopefully, this book has managed to present and explain the benefits that property-based testing can provide, but it is always possible to combine ScalaCheck with example-based testing or other testing techniques. Using tools like ScalaTest and sbt makes it very easy to do so.


Footnotes for Chapter 7:

[1] http://www.scalatest.org/user_guide/generator_driven_property_checks

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.178.151