Skip to content

Commit d3ad247

Browse files
authored
[ScalaCheck] Provide assertions for Prop and Properties (zio#8132)
This commit provides implicit methods for both ScalaCheck `Prop` and `Properties`. This enables beautiful things like the following: ```scala test("Monoid laws for Predicate") { import cats.kernel.laws.discipline.MonoidTests MonoidTests[MyCustomType].monoid.all.assertZIO() } @@ TestAspect.debug ``` The assertions are collecting the property names correctly, and show them when using the debug TestAspect.
1 parent 8f9ec4a commit d3ad247

File tree

2 files changed

+123
-4
lines changed

2 files changed

+123
-4
lines changed

test-scalacheck/shared/src/main/scala/zio/test/scalacheck/package.scala

+100-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ package zio.test
22

33
import org.scalacheck.rng.Seed
44
import org.scalacheck.Gen.Parameters
5+
import org.scalacheck.{Prop, Properties, Test => CheckTest}
56
import zio._
7+
import zio.test.{ErrorMessage => M}
68

79
/**
8-
* Provides functionality for converting legacy ScalaCheck generators to ZIO
9-
* Test generators to support upgrading to ZIO Test without having to
10-
* reimplement existing generators. To use it import this module and then call
11-
* `toGenZIO` on any existing ScalaCheck generator. For example:
10+
* This package provides helpers to integrate *some* ScalaCheck primitives to
11+
* their ZIO equivalents. Currently available helpers:
12+
*
13+
* - Converting ScalaCheck Generators to ZIO Generators
14+
* - Asserting a ScalaCheck `Prop` with ZIO
15+
* - Asserting a ScalaCheck `Properties` with ZIO
16+
*
17+
* **Generators**
18+
*
19+
* This functionality converts legacy ScalaCheck generators to ZIO Test
20+
* generators to support upgrading to ZIO Test without having to reimplement
21+
* existing generators. To use it import this module and then call `toGenZIO` on
22+
* any existing ScalaCheck generator. For example:
1223
*
1324
* {{{
1425
* import org.scalacheck.Arbitrary
@@ -20,6 +31,55 @@ import zio._
2031
* val anyInt: Gen[Any, Int] =
2132
* Arbitrary.arbitrary[Int].toGenZIO
2233
* }}}
34+
*
35+
* **Asserting ScalaCheck `Prop` and `Properties`**
36+
*
37+
* This functionality generates ZIO Assertions from either ScalaCheck `Prop` or
38+
* `Properties`. This helps with integrating other libraries that provide
39+
* ScalaCheck properties as helpers, i.e. cats-laws.
40+
*
41+
* `Prop` example:
42+
*
43+
* {{{
44+
* import org.scalacheck.Prop
45+
* import org.scalacheck.Test.{ Parameters => ScalaCheckParameters }
46+
*
47+
* import zio._
48+
* import zio.test._
49+
* import zio.test.scalacheck._
50+
*
51+
* val prop: Prop = Prop.forAll { (n: Int, m: Int) =>
52+
* n + m == m + n
53+
* }
54+
* val resultDefault: TestResult = prop.assertZIO()
55+
*
56+
* val resultWithCustomizations: TestResult =
57+
* prop.assertZIO("My Prop Name", ScalaCheckParameters.default.withMaxSize(10))
58+
* }}}
59+
*
60+
* `Properties` example:
61+
*
62+
* {{{
63+
* import org.scalacheck.{ Prop, Properties }
64+
* import org.scalacheck.Test.{ Parameters => ScalaCheckParameters }
65+
*
66+
* import zio._
67+
* import zio.test._
68+
* import zio.test.scalacheck._
69+
*
70+
* object MyProperties extends Properties("MyProperties") {
71+
* property("myProp") = Prop.forAll { (n: Int, m: Int) =>
72+
* n + m == m + n
73+
* }
74+
* }
75+
*
76+
* * val resultDefault: TestResult = MyProperties.assertZIO()
77+
*
78+
* // Beware that we can't provide a custom name here, it will be
79+
* // taken from the `Properties` name parameter
80+
* val resultWithCustomizations: TestResult =
81+
* MyProperties.assertZIO(ScalaCheckParameters.default.withMaxSize(10))
82+
* }}}
2383
*/
2484
package object scalacheck {
2585

@@ -37,4 +97,40 @@ package object scalacheck {
3797
} yield a
3898
}
3999
}
100+
101+
implicit final class ScalaCheckPropSyntax(private val self: Prop) extends AnyVal {
102+
103+
/**
104+
* Asserts a ScalaCheck `Prop` and provides a ZIO `TestResult`.
105+
*/
106+
def assertZIO(
107+
name: String = "ScalaCheck Prop Assertion",
108+
testParams: CheckTest.Parameters = CheckTest.Parameters.default
109+
): TestResult =
110+
Assertion.assertion[Prop](name)(prop => CheckTest.check(testParams, prop).passed).run(self)
111+
}
112+
113+
implicit final class ScalaCheckPropertiesSyntax(private val self: Properties) extends AnyVal {
114+
115+
/**
116+
* Asserts ScalaCheck `Properties` and provides a ZIO `TestResult`.
117+
*/
118+
def assertZIO(
119+
testParams: CheckTest.Parameters = CheckTest.Parameters.default
120+
): TestResult =
121+
Assertion(
122+
TestArrow
123+
.make[Properties, Boolean] { props =>
124+
CheckTest
125+
.checkProperties(testParams, props)
126+
.map { case (name, result) =>
127+
TestTrace.boolean(result.passed) {
128+
M.text(name) + M.choice("succeeded", "failed")
129+
}
130+
}
131+
.reduce(_ && _)
132+
}
133+
.withCode(self.name)
134+
).run(self)
135+
}
40136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package zio.test.scalacheck
2+
3+
import org.scalacheck.{Prop, Properties}
4+
import zio.Scope
5+
import zio.test._
6+
7+
object AssertionSpec extends ZIOSpecDefault {
8+
object FailingProperties extends Properties("MyProperties") {
9+
property("PassingProp") = Prop.propBoolean(false)
10+
}
11+
12+
object PassingProperties extends Properties("MyProperties") {
13+
property("FailingProp") = Prop.propBoolean(true)
14+
}
15+
16+
override def spec: Spec[TestEnvironment with Scope, Any] =
17+
suite("ZIO assertions for ScalaCheck")(
18+
test("Prop passing")(Prop.propBoolean(true).assertZIO()),
19+
test("Prop failing")(Prop.propBoolean(false).assertZIO()) @@ TestAspect.failing,
20+
test("Properties passing")(PassingProperties.assertZIO()),
21+
test("Properties failing")(FailingProperties.assertZIO()) @@ TestAspect.failing
22+
)
23+
}

0 commit comments

Comments
 (0)