Skip to content

Repository files navigation

ScalaCheck Effect

Continuous Integration Discord Latest version

ScalaCheck Effect is a library that extends the functionality of ScalaCheck to support "effectful" properties. An effectful property is one that evaluates each sample in some type constructor F[_]. For example:

import org.scalacheck.effect.PropF
import org.scalacheck.Test
import cats.effect.{ExitCode, IO, IOApp}

object Example extends IOApp {
  def run(args: List[String]): IO[ExitCode] = {
    val p: PropF[IO] = 
      PropF.forAllF { (x: Int) =>
        IO(x).map(res => assert(res == x))
      }

    val result: IO[Test.Result] = p.check()

    result.flatMap(r => IO(println(r))).as(ExitCode.Success)
  }
}

Running this program results in the output: Result(Passed,100,0,Map(),0).

This library provides the org.scalacheck.effect.PropF type, which is the effectul analog to org.scalacheck.Prop. In this example, we use PropF.forAllF to write a property of the shape Int => IO[Unit]. This example uses cats.effect.IO as the type constructor, but any effect F[_] with an instance of MonadError[F, Throwable] can be used, including scala.concurrent.Future.

The key idea here is using the PropF.{forAllF, forAllNoShrinkF} methods to create PropF[F] instances. The check() method on PropF converts a PropF[F] to a F[Test.Result].

sbt dependency

libraryDependencies += "org.typelevel" %% "scalacheck-effect" % scalacheckEffectVersion

MUnit Integration

This project also provides support for checking PropF values from within MUnit based test suites. To use scalacheck-effect with munit, add the following dependency to your build:

libraryDependencies += "org.typelevel" %% "scalacheck-effect-munit" % scalacheckEffectVersion % Test

The following usage example is for Cats Effect 2:

import munit.{CatsEffectSuite, ScalaCheckEffectSuite}
import org.scalacheck.effect.PropF

// Example uses https://github.com/typelevel/munit-cats-effect

class ExampleSuite extends CatsEffectSuite with ScalaCheckEffectSuite {
  test("first PropF test") {
    PropF.forAllF { (x: Int) =>
      IO(x).start.flatMap(_.join).map(res => assert(res == x))
    }
  }
}

For Cats Effect 3, the join becomes joinWithNever, so the PropF in the above example becomes:

PropF.forAllF { (x: Int) =>
  IO(x).start.flatMap(_.joinWithNever).map(res => assert(res == x))
}

For more details on the differences between Cats Effect 2 and 3 see the Cats Effect 3.x Migration Guide.

Design Goals

  • Support effectful properties without blocking.
  • Compatibility with Gen/Cogen/Arbitrary.
  • Parity with Prop features, including shrinking.
  • Follow same style as ScalaCheck and use ScalaCheck reporting.
  • Integrate well with popular test frameworks.
  • Non-goal: provide direct support for checking effectful properties directly from SBT or from standalone app.

Frequently Asked Questions

Why not just call Await.result / unsafeRunSync inside a property definition?

Calling Await.result, unsafeRunSync() or a similar blocking operation is not possible on Scala.js.

How to override the default ScalaCheck test parameters?

By overriding the scalaCheckTestParameters from the super ScalaCheckSuite.

override def scalaCheckTestParameters =
  super.scalaCheckTestParameters.withMinSuccessfulTests(20)

Acknowledgements

This library builds heavily on the ideas in ScalaCheck. It grew out of the FS2 AsyncPropertySuite, which only implemented a handful of features. The Weaver Test framework also has similar support for effectful properties. Finally, the Scala Hedgehog library has a prototype of similar functionality.

About

Effectful property testing built on ScalaCheck

Resources

Code of conduct

Security policy

Stars

82 stars

Watchers

10 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages