ScalaCheck integration

Weaver comes with basic ScalaCheck integration, allowing for property-based testing.

Installation

You'll need to install an additional dependency in order to use ScalaCheck with Weaver.

SBT

libraryDependencies +=  "org.typelevel" %% "weaver-scalacheck" % "0.13.0" % Test

Mill

object test extends Tests {
  def ivyDeps = Agg(
    ivy"org.typelevel::weaver-scalacheck:0.13.0"
  )
}

Usage

Add the weaver.scalacheck.Checkers mixin to use ScalaCheck within your test suite.

import org.scalacheck.Gen

import weaver._
import weaver.scalacheck._

object ForallExamples extends SimpleIOSuite with Checkers {

  // CheckConfig can be overridden at the suite level
  override def checkConfig: CheckConfig =
    super.checkConfig.copy(perPropertyParallelism = 100)

  // Using a single `Gen` instance
  test("Single Gen form") {
    // Takes a single, explicit `Gen` instance
    forall(Gen.posNum[Int]) { a =>
      expect(a > 0)
    }
  }

  // There is only one overload for the `forall` that takes an explicit `Gen` parameter
  // To use multiple `Gen` instances, compose them monadically before passing to `forall`
  test("Multiple Gen form") {
    // Compose into a single `Gen[(Int, Int)]`
    val gen = for {
      a <- Gen.posNum[Int]
      b <- Gen.posNum[Int]
    } yield (a, b)

    // Unapply the tuple to access individual members
    forall(gen) { case (a, b) =>
      expect(a > 0) and expect(b > 0)
    }
  }

  // Using a number of `Arbitrary` instances
  test("Arbitrary form") {
    // There are 6 overloads, to pass 1-6 parameters
    forall { (a1: Int, a2: Int, a3: Int) =>
      expect(a1 * a2 * a3 == a3 * a2 * a1)
    }
  }
  
  test("Failure example") {
    // There are 6 overloads, to pass 1-6 parameters
    forall { (a1: Int, a2: Int) =>
      expect(a1 + a2 % 2 == 0)
    }
  }

  test("CheckConfig at test level") {
    // CheckConfig can be overridden locally at the test level
    forall.withConfig(CheckConfig.default.copy(perPropertyParallelism = 1)) {
      (x: Int) => expect(x > 0)
    }
  }

}
ForallExamples
+ Single Gen form 45ms
+ Multiple Gen form 40ms
+ Arbitrary form 34ms
- Failure example 11ms
- CheckConfig at test level 4ms

*************FAILURES*************
ForallExamples
- Failure example 11ms
 [0] Property test failed on try 1 with input:
 [0] 564760060
 [0] 1
 [0] You can reproduce this by adding the following configuration to your test:
 [0] 
 [0] forall.withConfig(checkConfig.withInitialSeed(org.scalacheck.rng.Seed.fromBase64("uHL838okxd9qvxJaFqiDAHGVyL0HrQANQ1hsa01UvHK=").toOption))
 [0] 
 [0] scalacheck.md#L56
 [0]     forall { (a1: Int, a2: Int) =>
 [0]            ^


 [1] assertion failed
 [1] 
 [1] expect(a1 + a2 % 2 == 0)
 [1] 
 [1] Use the `clue` function to troubleshoot
 [1] 
 [1] scalacheck.md#L57
 [1]       expect(a1 + a2 % 2 == 0)
 [1]             ^
- CheckConfig at test level 4ms
 [0] Property test failed on try 1 with input:
 [0] -2147483648
 [0] You can reproduce this by adding the following configuration to your test:
 [0] 
 [0] forall.withConfig(checkConfig.withInitialSeed(org.scalacheck.rng.Seed.fromBase64("LTeGX6AJ_fyivabecgBMiAUNgWLcBsRUWwGFn1kNiWM=").toOption))
 [0] 
 [0] scalacheck.md#L63
 [0]     forall.withConfig(CheckConfig.default.copy(perPropertyParallelism = 1)) {
 [0]                                                                             ^


 [1] assertion failed
 [1] 
 [1] expect(x > 0)
 [1] 
 [1] Use the `clue` function to troubleshoot
 [1] 
 [1] scalacheck.md#L64
 [1]       (x: Int) => expect(x > 0)
 [1]                         ^
Total 5, Failed 2, Passed 3, Ignored 0