Last active
June 4, 2025 16:22
-
-
Save Baccata/bc05593d1c1f55ea4f9fdb53fbd0cc8d to your computer and use it in GitHub Desktop.
Something to make it easier to write scalacheck
This file contains hidden or 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
package xen | |
import org.scalacheck.* | |
import org.scalacheck.util.Pretty | |
import munit.ScalaCheckSuite | |
import munit.TestOptions | |
/** | |
* This constructs aims at allowing to lift `Gen` instances whilst capturing the associated `Shrink` and `Pretty` | |
* at the time of lifting. This allows to interpret into `Prop` easily, and thus makes it easier to write tests | |
* with complex sequential generators chains. | |
* | |
* Before : | |
* | |
* {{ | |
* val gen = for { | |
* a <- someGenA | |
* b <- someGenB(a) | |
* c <- someGenC(b) | |
* } yield (a, b, c) | |
* | |
* Prop.forAll(gen){ case (a, b, c) = | |
* assertOn(a, b, c) | |
* } | |
* }} | |
* | |
* After : | |
* | |
* {{ | |
* val xen = for { | |
* a <- someGenA.xen | |
* b <- someGenB(a).xen | |
* c <- someGenC(b).xen | |
* } yield { | |
* assertOn(a, b, c) | |
* } | |
* | |
* Xen.toProp(xen) | |
* }} | |
* | |
*/ | |
sealed trait Xen[A] extends Product with Serializable { | |
final def flatMap[B](f: A => Xen[B]): Xen[B] = Xen.FlatMap(this, f) | |
final def map[B](f: A => B): Xen[B] = Xen.FlatMap(this, (a: A) => Xen.Pure(f(a))) | |
} | |
object Xen { | |
trait Suite extends ScalaCheckSuite { | |
implicit def toOps[A](genA: Gen[A]): GenOps[A] = new GenOps(genA) | |
def test[P](name: String)(xen: Xen[P])(implicit ev: P => Prop): Unit = | |
super.test(TestOptions(name))(toProp(xen)) | |
def test[P](options: TestOptions)(xen: Xen[P])(implicit ev: P => Prop): Unit = | |
super.test(options)(toProp(xen)) | |
} | |
class GenOps[A](private val gen: Gen[A]) extends AnyVal { | |
def xen(implicit shrink: Shrink[A], pretty: A => Pretty): Xen[A] = Lifted(gen, shrink, pretty) | |
} | |
private final case class Lifted[A](gen: Gen[A], shrink: Shrink[A], pretty: A => Pretty) extends Xen[A] | |
private final case class Pure[A](a: A) extends Xen[A] | |
private final case class FlatMap[A, B](fa: Xen[A], f: A => Xen[B]) extends Xen[B] | |
def toProp[P](xen: Xen[P])(implicit prop: P => Prop): Prop = { | |
def aux[A](xen: Xen[A])(toProp: A => Prop): Prop = xen match { | |
case Lifted(gen, shrink, pretty) => Prop.forAll(gen)(identity)(toProp, shrink, pretty) | |
case Pure(a) => toProp(a) | |
case FlatMap(fa, f) => aux(fa)(aa => aux(f(aa))(toProp)) | |
} | |
aux(xen)(prop) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment