From 8dc1b0cddc731b83161f316556ce426f042e8238 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Tue, 22 Aug 2023 21:18:36 +0800 Subject: [PATCH 1/8] Added pure style traits to selecting style traits page. --- .../userGuide/selectingAStyle.scala.html | 185 +++++++++++++++++- 1 file changed, 183 insertions(+), 2 deletions(-) diff --git a/app/views/userGuide/selectingAStyle.scala.html b/app/views/userGuide/selectingAStyle.scala.html index d6427e06a4..ad1c932178 100644 --- a/app/views/userGuide/selectingAStyle.scala.html +++ b/app/views/userGuide/selectingAStyle.scala.html @@ -46,7 +46,7 @@

Selecting testing styles for your project

-The style you choose dictates only how the declarations of your tests will look. Everything else in ScalaTest—assertions, matchers, +The style you choose dictates only how the declarations of your tests will look. Everything else in ScalaTest—assertions, expectations, matchers, mixin traits, etc.—works consistently the same way no matter what style you chose.

@@ -93,6 +93,30 @@

The FunSuite style

 libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "@{latestVersion}" % "test"
 
+ +

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest.funsuite.FunSuite
+import org.scalatest.expectations.Expectations._
+
class SetSuite extends FunSuite { +
test("An empty Set should have size 0") { + expect(Set.empty.size == 0) + } +
test("Invoking head on an empty Set should produce NoSuchElementException") { + expectThrows[NoSuchElementException] { + Set.empty.head + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -103,6 +127,7 @@

The FlatSpec style

flat like xUnit, so simple and familiar, but the test names must be written in a specification style: "X should Y," "A must B," etc.

 import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.expectations.Expectations._
 
class SetSpec extends AnyFlatSpec {
"An empty Set" should "have size 0" in { assert(Set.empty.size == 0) @@ -120,6 +145,29 @@

The FlatSpec style

libraryDependencies += "org.scalatest" %% "scalatest-flatspec" % "@{latestVersion}" % "test"
+

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest.flatspec.FlatSpec
+import org.scalatest.expectations.Expectations._
+
class SetSpec extends FlatSpec { +
"An empty Set" should "have size 0" in { + expect(Set.empty.size == 0) + } +
it should "produce NoSuchElementException when head is invoked" in { + expectThrows[NoSuchElementException] { + Set.empty.head + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -154,6 +202,33 @@

The FunSpec style

libraryDependencies += "org.scalatest" %% "scalatest-funspec" % "@{latestVersion}" % "test" +

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest.funspec.FunSpec
+import org.scalatest.expectations.Expectations._
+
class SetSpec extends FunSpec { +
describe("A Set") { + describe("when empty") { + it("should have size 0") { + assert(Set.empty.size == 0) + } +
it("should produce NoSuchElementException when head is invoked") { + assertThrows[NoSuchElementException] { + Set.empty.head + } + } + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -185,6 +260,34 @@

The WordSpec style

 libraryDependencies += "org.scalatest" %% "scalatest-wordspec" % "@{latestVersion}" % "test"
 
+ +

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest.wordspec.WordSpec
+import org.scalatest.expectations.Expectations._
+
class SetSpec extends WordSpec { +
"A Set" when { + "empty" should { + "have size 0" in { + expect(Set.empty.size == 0) + } +
"produce NoSuchElementException when head is invoked" in { + expectThrows[NoSuchElementException] { + Set.empty.head + } + } + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -216,6 +319,33 @@

The FreeSpec style

libraryDependencies += "org.scalatest" %% "scalatest-freespec" % "@{latestVersion}" % "test" +

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest.freespec.FreeSpec
+import org.scalatest.expectations.Expectations._
+
class SetSpec extends FreeSpec { +
"A Set" - { + "when empty" - { + "should have size 0" in { + expect(Set.empty.size == 0) + } +
"should produce NoSuchElementException when head is invoked" in { + expectThrows[NoSuchElementException] { + Set.empty.head + } + } + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -229,10 +359,11 @@

The PropSpec style

import org.scalatest._ import matchers._ import prop._ +import propspec.AnyPropSpec import scala.collection.immutable._
class SetSpec extends AnyPropSpec with TableDrivenPropertyChecks with should.Matchers {
val examples = - Table( + Table[Set[Int]]( "set", BitSet.empty, HashSet.empty[Int], @@ -266,6 +397,7 @@

The FeatureSpec style

 import org.scalatest._
+import featurespec.AnyFeatureSpec
 
class TVSet { private var on: Boolean = false def isOn: Boolean = on @@ -301,12 +433,59 @@

The FeatureSpec style

} }
+

To select just the FeatureSpec style in an sbt build, include this line:

 libraryDependencies += "org.scalatest" %% "scalatest-featurespec" % "@{latestVersion}" % "test"
 
+

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest._
+import featurespec.FeatureSpec
+import expectations.Expectations._
+
class TVSet { + private var on: Boolean = false + def isOn: Boolean = on + def pressPowerButton() { + on = !on + } +} +
class TVSetSpec extends FeatureSpec with GivenWhenThen { +
info("As a TV set owner") + info("I want to be able to turn the TV on and off") + info("So I can watch TV when I want") + info("And save energy when I'm not watching TV") +
feature("TV power button") { + scenario("User presses power button when TV is off") { +
Given("a TV set that is switched off") + val tv = new TVSet +
When("the power button is pressed") + tv.pressPowerButton() +
Then("the TV should switch on") + expect(tv.isOn) + } +
scenario("User presses power button when TV is on") { +
Given("a TV set that is switched on") + val tv = new TVSet + tv.pressPowerButton() +
When("the power button is pressed") + tv.pressPowerButton() +
Then("the TV should switch off") + expect(!tv.isOn) + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ @@ -344,6 +523,8 @@

RefSpec (JVM only)

libraryDependencies += "org.scalatest" %% "scalatest-refspec" % "@{latestVersion}" % "test" +

RefSpec does not support expectations.

+ From bb143d061cdc9fbb4baec7b7b69173e7ca7dba0b Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Sat, 26 Aug 2023 16:15:31 +0800 Subject: [PATCH 2/8] Added pure style trait guide for PropSpec. --- .../userGuide/selectingAStyle.scala.html | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/app/views/userGuide/selectingAStyle.scala.html b/app/views/userGuide/selectingAStyle.scala.html index ad1c932178..83f5ebcf21 100644 --- a/app/views/userGuide/selectingAStyle.scala.html +++ b/app/views/userGuide/selectingAStyle.scala.html @@ -387,6 +387,41 @@

The PropSpec style

libraryDependencies += "org.scalatest" %% "scalatest-propspec" % "@{latestVersion}" % "test" +

If you would like to use expectations instead of assertions, you may use the pure style:

+ +
+import org.scalatest._
+import prop._
+import propspec.PropSpec
+import org.scalatest.expectations.Expectations._
+import scala.collection.immutable._
+
class SetSpec extends PropSpec with TableDrivenPropertyChecks { +
val examples = + Table[Set[Int]]( + "set", + BitSet.empty, + HashSet.empty[Int], + TreeSet.empty[Int] + ) +
property("an empty Set should have size 0") { + forAll(examples) { set => + expect(set.size == 0) + } + } +
property("invoking head on an empty set should produce NoSuchElementException") { + forAll(examples) { set => + expectThrows[NoSuchElementException] { set.head } + } + } +} +
+ +

In order to use expectations you'll need add the following line in sbt build:

+ +
+libraryDependencies += "org.scalatest" %% "scalatest-expectations" % "@{latestVersion}" % "test"
+
+ From 0475be7b34b92e9df032f96c392082e05f8ebe75 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Wed, 30 Aug 2023 22:56:46 +0800 Subject: [PATCH 3/8] Initial user guide adjustments for ScalaTest generators. --- app/controllers/UserGuide.scala | 12 + app/views/plus/scalacheck.scala.html | 2 +- .../generatorDrivenPropertyChecks.scala.html | 65 ++- .../userGuide/propertyBasedTesting.scala.html | 32 +- .../scalacheckDrivenPropertyChecks.scala.html | 414 ++++++++++++++++++ .../scalacheckPropertyBasedTesting.scala.html | 177 ++++++++ ...acheckTableDrivenPropertyChecks.scala.html | 283 ++++++++++++ .../tableDrivenPropertyChecks.scala.html | 6 +- conf/routes | 3 + 9 files changed, 931 insertions(+), 63 deletions(-) create mode 100644 app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html create mode 100644 app/views/userGuide/scalacheckPropertyBasedTesting.scala.html create mode 100644 app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html diff --git a/app/controllers/UserGuide.scala b/app/controllers/UserGuide.scala index d8af54a114..109668f413 100644 --- a/app/controllers/UserGuide.scala +++ b/app/controllers/UserGuide.scala @@ -69,6 +69,10 @@ class UserGuide @Inject() (cc: ControllerComponents) extends AbstractController( Ok(views.html.userGuide.propertyBasedTesting()) } + def scalacheckPropertyBasedTesting = Action { + Ok(views.html.userGuide.scalacheckPropertyBasedTesting()) + } + def otherGoodies = Action { Ok(views.html.userGuide.otherGoodies()) } @@ -97,10 +101,18 @@ class UserGuide @Inject() (cc: ControllerComponents) extends AbstractController( Ok(views.html.userGuide.tableDrivenPropertyChecks()) } + def scalacheckTableDrivenPropertyChecks = Action { + Ok(views.html.userGuide.scalacheckTableDrivenPropertyChecks()) + } + def generatorDrivenPropertyChecks = Action { Ok(views.html.userGuide.generatorDrivenPropertyChecks()) } + def scalacheckDrivenPropertyChecks = Action { + Ok(views.html.userGuide.scalacheckDrivenPropertyChecks()) + } + def writingScalacheckStyleProperties = Action { Ok(views.html.userGuide.writingScalacheckStyleProperties()) } diff --git a/app/views/plus/scalacheck.scala.html b/app/views/plus/scalacheck.scala.html index a342ec7c6e..c35ad5db36 100644 --- a/app/views/plus/scalacheck.scala.html +++ b/app/views/plus/scalacheck.scala.html @@ -45,7 +45,7 @@

ScalaTest + ScalaCheck

-You may also refer to Property-based testing for detailed usage documentation. +You may also refer to Property-based testing using ScalaCheck for detailed usage documentation.

diff --git a/app/views/userGuide/generatorDrivenPropertyChecks.scala.html b/app/views/userGuide/generatorDrivenPropertyChecks.scala.html index 0fcdfdeb16..639aeea145 100644 --- a/app/views/userGuide/generatorDrivenPropertyChecks.scala.html +++ b/app/views/userGuide/generatorDrivenPropertyChecks.scala.html @@ -22,25 +22,23 @@

Generator-driven property checks

-To use generator-driven property checks, you must mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - (previously known as org.scalatest.props.PropertyChecks). If you are also using table-driven property checks, you can mix in -trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks, -which extends both ScalaCheckDrivenPropertyChecks and +To use generator-driven property checks, you must mix in trait org.scalatest.prop.GeneratorDrivenPropertyChecks. +If you are also using table-driven property checks, you can mix in +trait org.scalatestplus.prop.PropertyChecks, +which extends both GeneratorDrivenPropertyChecks and TableDrivenPropertyChecks. -Generator-driven property checks uses ScalaCheck and -ScalaTest + ScalaCheck, so you must also include both jars on your classpath when -you compile and run your tests. +Starting ScalaTest 3.3 release, generator-driven property checks uses ScalaTest generators.

-Trait ScalaCheckDrivenPropertyChecks contains forAll methods that provide various ways to check properties using +Trait GeneratorDrivenPropertyChecks contains forAll methods that provide various ways to check properties using generated data. It also contains a wherever method that can be used to indicate a property need only hold whenever some condition is true.

-For an example of trait ScalaCheckDrivenPropertyChecks in action, imagine you want to test this Fraction class: +For an example of trait GeneratorDrivenPropertyChecks in action, imagine you want to test this Fraction class:

@@ -55,7 +53,7 @@ 

Generator-driven property checks

-To test the behavior of Fraction, you could mix in or import the members of ScalaCheckDrivenPropertyChecks +To test the behavior of Fraction, you could mix in or import the members of GeneratorDrivenPropertyChecks (and should.Matchers) and check a property using a forAll method, like this:

@@ -76,21 +74,20 @@

Generator-driven property checks

-Trait ScalaCheckDrivenPropertyChecks provides overloaded forAll methods -that allow you to check properties using the data provided by a ScalaCheck generator. The simplest form +Trait GeneratorDrivenPropertyChecks provides overloaded forAll methods +that allow you to check properties using the data provided by a ScalaTest generator. The simplest form of forAll method takes two parameter lists, the second of which is implicit. The first parameter list -is a "property" function with one to six parameters. An implicit Arbitrary generator and Shrink object needs to be supplied for +is a "property" function with one to six parameters. An implicit PropertyCheckConfiguration, one to six org.scalatest.prop.Generator, org.scalatest.enablers.PropCheckerAsserting, org.scalactic.Prettifier and org.scalactic.source.Position object needs to be supplied for The forAll method will pass each row of data to -each parameter type. ScalaCheck provides many implicit Arbitrary generators for common types such as -Int, String, List[Float], etc., in its org.scalacheck.Arbitrary companion -object. So long as you use types for which ScalaCheck already provides implicit Arbitrary generators, you needn't -worry about them. Same for Shrink objects, which are provided by ScalaCheck's org.scalacheck.Shrink companion -object. Most often you can simply pass a property function to forAll, and the compiler will grab the implicit -values provided by ScalaCheck. +each parameter type. ScalaTest provides many implicit org.scalatest.prop.Generator generators for common types such as +Int, String, List[Float], PosInt etc., in its org.scalatest.prop.CommonGenerators companion +object. So long as you use types for which ScalaTest generators already provides implicit org.scalatest.prop.Generator, you needn't +worry about them. Most often you can simply pass a property function to forAll, and the compiler will grab the implicit +values provided by ScalaTest.

-The forAll methods use the supplied Arbitrary generators to generate example +The forAll methods use the supplied org.scalatest.prop.Generators to generate example arguments and pass them to the property function, and generate a GeneratorDrivenPropertyCheckFailedException if the function completes abruptly for any exception that would normally cause a test to @@ -152,7 +149,7 @@

Generator-driven property checks

Supplying generators

-ScalaCheck provides a nice library of compositors that makes it easy to create your own custom generators. If you +ScalaTest provides a nice library of compositors that makes it easy to create your own custom generators. If you want to supply custom generators to a property check, place them in parentheses after forAll, before the property check function (a curried form of forAll).

@@ -162,8 +159,8 @@

Generator-driven property checks

-import org.scalacheck.Gen
-
val evenInts = for (n <- Gen.choose(-1000, 1000)) yield 2 * n +import org.scalatest.prop.CommonGenerators +
val evenInts = for (n <- CommonGenerators.between(-1000, 1000)) yield 2 * n

@@ -175,7 +172,7 @@

Generator-driven property checks

-Custom generators are necessary when you want to pass data types not supported by ScalaCheck's arbitrary generators, +Custom generators are necessary when you want to pass data types not supported by ScalaTest's generators, but are also useful when some of the values in the full range for the passed types are not valid. For such values you would use a whenever clause. In the Fraction class shown above, neither the passed numerator or denominator can be Integer.MIN_VALUE, and the passed denominator cannot be zero. This shows up in the @@ -193,7 +190,7 @@

Generator-driven property checks

 val validNumers =
-  for (n <- Gen.choose(Integer.MIN_VALUE + 1, Integer.MAX_VALUE)) yield n
+  for (n <- CommonGenerators.between(Integer.MIN_VALUE + 1, Integer.MAX_VALUE)) yield n
 val validDenoms =
   for (d <- validNumers if d != 0) yield d
 
@@ -220,8 +217,8 @@

Generator-driven property checks

Note that even if you are use generators that don't produce the invalid values, you still need the -whenever clause. The reason is that once a property fails, ScalaCheck will try and shrink -the values to the smallest values that still cause the property to fail. During this shrinking process ScalaCheck +whenever clause. The reason is that once a property fails, ScalaTest will try and shrink +the values to the smallest values that still cause the property to fail. During this shrinking process ScalaTest may pass invalid values. The whenever clause is still needed to guard against those values. (The whenever clause also clarifies to readers of the code exactly what the property is in a succinct way, without requiring that they find and understand the generator definitions.) @@ -294,13 +291,13 @@

Generator-driven property checks

-maxDiscarded +maxDiscardedFactor -50 +5.0 -the maximum number of discarded property evaluations allowed during a property check +the factor of discarded property evaluations allowed during property evaluation. @@ -381,7 +378,7 @@

Generator-driven property checks

-forAll (minSuccessful(500), maxDiscarded(300)) { (n: Int, d: Int) => ...
+forAll (minSuccessful(500), maxDiscardedFactor(2.5)) { (n: Int, d: Int) => ...
 

@@ -391,13 +388,13 @@

Generator-driven property checks

 // If providing argument names
-forAll ("n", "d", minSuccessful(500), maxDiscarded(300)) {
+forAll ("n", "d", minSuccessful(500), maxDiscardedFactor(2.5)) {
   (n: Int, d: Int) => ...
 
// If providing generators -forAll (validNumers, validDenoms, minSuccessful(500), maxDiscarded(300)) { +forAll (validNumers, validDenoms, minSuccessful(500), maxDiscardedFactor(2.5)) { (n: Int, d: Int) => ...
// If providing (<generators>, <name>) pairs -forAll ((validNumers, "n"), (validDenoms, "d"), minSuccessful(500), maxDiscarded(300)) { +forAll ((validNumers, "n"), (validDenoms, "d"), minSuccessful(500), maxDiscardedFactor(2.5)) { (n: Int, d: Int) => ...
diff --git a/app/views/userGuide/propertyBasedTesting.scala.html b/app/views/userGuide/propertyBasedTesting.scala.html index add61e2ed2..ff8fcba7e2 100644 --- a/app/views/userGuide/propertyBasedTesting.scala.html +++ b/app/views/userGuide/propertyBasedTesting.scala.html @@ -37,34 +37,17 @@

Property-based testing

In ScalaTest, properties are specified as functions and the data points used to check properties can be supplied by either tables -or generators. Generator-driven property checks are performed via integration with ScalaCheck through -ScalaTest + ScalaCheck library, by including: +or generators. Starting from ScalaTest 3.3, generator-driven property checks are performed via ScalaTest generators. +If you are using ScalaCheck, you may want to refer to ScalaCheck-driven property checks.

-
-libraryDependencies += "org.scalatestplus" %% "scalacheck-@{latestScalaCheckVersion}" % "@{latestScalaCheckPlusVersion}" % "test"
-
- -

-in your SBT file, or if you use Maven: -

- -
-<dependency>
-    <groupId>org.scalatestplus</groupId>
-    <artifactId>scalacheck-@{latestScalaCheckVersion}_2.13</artifactId>
-    <version>@latestScalaCheckPlusVersion</version>
-    <scope>test</scope>
-</dependency>    
-
-

-To use this style of testing, mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks (previously known as org.scalatest.props.PropertyChecks, deprecated in 3.0.6), or import the +To use this style of testing, mix in trait org.scalatest.prop.PropertyChecks, or import the members of its companion object.

-As an example property-based testing using both table- and generator-driven properties, imagine you want to test this Fraction class: +As an example property-based testing using both table and generator-driven properties, imagine you want to test this Fraction class:

@@ -79,7 +62,7 @@ 

Property-based testing

-If you mix in ScalaCheckPropertyChecks, you could use a generator-driven property check to test that the passed values for numerator and +If you mix in PropertyChecks, you could use a generator-driven property check to test that the passed values for numerator and denominator are properly normalized, like this:

@@ -104,8 +87,8 @@

Property-based testing

passed as n and d. (This property specifies how the numerator and denominator passed to the Fraction constructor should be normalized.) The whenever clause indicates invalid values for n and d. -Any other values are valid. When run, ScalaCheck generators will be passed implicitly to forAll and supply integer values for -n and d. By default the property will be checked against 10 valid pairs of n and d. Note that this is different from ScalaCheck's default of 100. +Any other values are valid. When run, ScalaTest generators will be passed implicitly to forAll and supply integer values for +n and d. By default the property will be checked against 10 valid pairs of n and d.

@@ -166,7 +149,6 @@

Property-based testing

diff --git a/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html b/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html new file mode 100644 index 0000000000..13ec3dff6a --- /dev/null +++ b/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html @@ -0,0 +1,414 @@ +@* +* Copyright 2010-2017 Artima, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*@ + +@import controllers.Application.scaladocsPageUrl + +@userGuidePage("Generator-driven property checks") { +

+ +

ScalaCheck-driven property checks

+ +

+ To use generator-driven property checks, you must mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + (previously known as org.scalatest.props.PropertyChecks). If you are also using table-driven property checks, you can mix in + trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks, + which extends both ScalaCheckDrivenPropertyChecks and + TableDrivenPropertyChecks. + Generator-driven property checks uses ScalaCheck and + ScalaTest + ScalaCheck, so you must also include both jars on your classpath when + you compile and run your tests. +

+ +

+ Trait ScalaCheckDrivenPropertyChecks contains forAll methods that provide various ways to check properties using + generated data. + It also contains a wherever method that can be used to indicate a property need only hold whenever + some condition is true. +

+ +

+ For an example of trait ScalaCheckDrivenPropertyChecks in action, imagine you want to test this Fraction class: +

+ +
+class Fraction(n: Int, d: Int) {
+
require(d != 0) + require(d != Integer.MIN_VALUE) + require(n != Integer.MIN_VALUE) +
val numer = if (d < 0) -1 * n else n + val denom = d.abs +
override def toString = numer + " / " + denom +} +
+ +

+ To test the behavior of Fraction, you could mix in or import the members of ScalaCheckDrivenPropertyChecks + (and should.Matchers) and check a property using a forAll method, like this: +

+ +
+forAll { (n: Int, d: Int) =>
+
whenever (d != 0 && d != Integer.MIN_VALUE + && n != Integer.MIN_VALUE) { +
val f = new Fraction(n, d) +
if (n < 0 && d < 0 || n > 0 && d > 0) + f.numer should be > 0 + else if (n != 0) + f.numer should be < 0 + else + f.numer should be === 0 +
f.denom should be > 0 + } +} +
+ +

+ Trait ScalaCheckDrivenPropertyChecks provides overloaded forAll methods + that allow you to check properties using the data provided by a ScalaCheck generator. The simplest form + of forAll method takes two parameter lists, the second of which is implicit. The first parameter list + is a "property" function with one to six parameters. An implicit Arbitrary generator and Shrink object needs to be supplied for + The forAll method will pass each row of data to + each parameter type. ScalaCheck provides many implicit Arbitrary generators for common types such as + Int, String, List[Float], etc., in its org.scalacheck.Arbitrary companion + object. So long as you use types for which ScalaCheck already provides implicit Arbitrary generators, you needn't + worry about them. Same for Shrink objects, which are provided by ScalaCheck's org.scalacheck.Shrink companion + object. Most often you can simply pass a property function to forAll, and the compiler will grab the implicit + values provided by ScalaCheck. +

+ +

+ The forAll methods use the supplied Arbitrary generators to generate example + arguments and pass them to the property function, and + generate a GeneratorDrivenPropertyCheckFailedException if the function + completes abruptly for any exception that would normally cause a test to + fail in ScalaTest other than DiscardedEvaluationException. An + DiscardedEvaluationException, + which is thrown by the whenever method (defined in trait Whenever, which this trait extends) to indicate + a condition required by the property function is not met by a row + of passed data, will simply cause forAll to discard that row of data. +

+ +

Supplying argument names

+ +

+ You can optionally specify string names for the arguments passed to a property function, which will be used + in any error message when describing the argument values that caused the failure. To supply the names, place them in a comma separated list + in parentheses after forAll before the property function (a curried form of forAll). Here's + an example: +

+ +
+forAll ("a", "b") { (a: String, b: String) =>
+  a.length + b.length should equal ((a + b).length + 1) // Should fail
+}
+
+ +

+ When this fails, you'll see an error message that includes this: +

+ +
+Occurred when passed generated values (
+  a = "",
+  b = ""
+)
+
+ +

+ When you don't supply argument names, the error message will say arg0, arg1, etc.. + For example, this property check: +

+ +
+forAll { (a: String, b: String) =>
+  a.length + b.length should equal ((a + b).length + 1) // Should fail
+}
+
+ +

+ Will fail with an error message that includes: +

+ +
+Occurred when passed generated values (
+  arg0 = "",
+  arg1 = ""
+)
+
+ +

Supplying generators

+ +

+ ScalaCheck provides a nice library of compositors that makes it easy to create your own custom generators. If you + want to supply custom generators to a property check, place them in parentheses after forAll, before + the property check function (a curried form of forAll). +

+ +

+ For example, to create a generator of even integers between (and including) -2000 and 2000, you could write this: +

+ +
+import org.scalacheck.Gen
+
val evenInts = for (n <- Gen.choose(-1000, 1000)) yield 2 * n +
+ +

+ Given this generator, you could use it on a property check like this: +

+ +
+forAll (evenInts) { (n) => n % 2 should equal (0) }
+
+ +

+ Custom generators are necessary when you want to pass data types not supported by ScalaCheck's arbitrary generators, + but are also useful when some of the values in the full range for the passed types are not valid. For such values you + would use a whenever clause. In the Fraction class shown above, neither the passed numerator or + denominator can be Integer.MIN_VALUE, and the passed denominator cannot be zero. This shows up in the + whenever clause like this: +

+ +
+whenever (d != 0 && d != Integer.MIN_VALUE
+    && n != Integer.MIN_VALUE) { ...
+
+ +

+ You could in addition define generators for the numerator and denominator that only produce valid values, like this: +

+ +
+val validNumers =
+  for (n <- Gen.choose(Integer.MIN_VALUE + 1, Integer.MAX_VALUE)) yield n
+val validDenoms =
+  for (d <- validNumers if d != 0) yield d
+
+ +

+ You could then use them in the property check like this: +

+ +
+forAll (validNumers, validDenoms) { (n: Int, d: Int) =>
+
whenever (d != 0 && d != Integer.MIN_VALUE + && n != Integer.MIN_VALUE) { +
val f = new Fraction(n, d) +
if (n < 0 && d < 0 || n > 0 && d > 0) + f.numer should be > 0 + else if (n != 0) + f.numer should be < 0 + else + f.numer should be === 0 +
f.denom should be > 0 + } +} +
+ +

+ Note that even if you are use generators that don't produce the invalid values, you still need the + whenever clause. The reason is that once a property fails, ScalaCheck will try and shrink + the values to the smallest values that still cause the property to fail. During this shrinking process ScalaCheck + may pass invalid values. The whenever clause is still needed to guard against those values. (The + whenever clause also clarifies to readers of the code exactly what the property is in a succinct + way, without requiring that they find and understand the generator definitions.) +

+ +

Supplying both generators and argument names

+ +

+ If you want to supply both generators and named arguments, you can do so by providing a list of (<generator>, <name>) pairs + in parentheses after forAll, before the property function. Here's an example: +

+ +
+forAll ((validNumers, "n"), (validDenoms, "d")) { (n: Int, d: Int) =>
+
whenever (d != 0 && d != Integer.MIN_VALUE + && n != Integer.MIN_VALUE) { +
val f = new Fraction(n, d) +
if (n < 0 && d < 0 || n > 0 && d > 0) + f.numer should be > 0 + else if (n != 0) + f.numer should be < 0 + else + f.numer should be === 0 +
f.denom should be > 0 + } +} +
+ +

+ Were this property check to fail, it would mention the names n and d in the error message, like this: +

+ +
+Occurred when passed generated values (
+  n = 17,
+  d = 21
+)
+
+ +

Property check configuration

+ +

+ The property checks performed by the forAll methods of this trait can be flexibly configured via the services + provided by supertrait Configuration. The five configuration parameters for property checks along with their + default values and meanings are described in the following table: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Configuration Parameter + + Default Value + + Meaning +
+ minSuccessful + + 10 + + the minimum number of successful property evaluations required for the property to pass (Note that this is different from ScalaCheck's default of 100.) +
+ maxDiscardedFactor + + 5.0 + + the factor of discarded property evaluations allowed during property evaluation. +
+ minSize + + 0 + + the minimum size parameter to provide to ScalaCheck, which it will use when generating objects for which size matters (such as strings or lists) +
+ maxSize + + 100 + + the maximum size parameter to provide to ScalaCheck, which it will use when generating objects for which size matters (such as strings or lists) +
+ workers + + 1 + + specifies the number of worker threads to use during property evaluation +
+ +

+ The forAll methods of trait GeneratorDrivenPropertyChecks each take a PropertyCheckConfiguration + object as an implicit parameter. This object provides values for each of the five configuration parameters. Trait Configuration + provides an implicit val named generatorDrivenConfig with each configuration parameter set to its default value. + If you want to set one or more configuration parameters to a different value for all property checks in a suite you can override this + val (or hide it, for example, if you are importing the members of the GeneratorDrivenPropertyChecks companion object rather + than mixing in the trait.) For example, if + you want all parameters at their defaults except for minSize and maxSize, you can override + generatorDrivenConfig, like this: + +

+implicit override val generatorDrivenConfig =
+  PropertyCheckConfiguration(minSize = 10, maxSize = 20)
+
+ +

+ Or, if hide it by declaring a variable of the same name in whatever scope you want the changed values to be in effect: +

+ +
+implicit val generatorDrivenConfig =
+  PropertyCheckConfiguration(minSize = 10, maxSize = 20)
+
+ +

+ In addition to taking a PropertyCheckConfiguration object as an implicit parameter, the forAll methods of trait + GeneratorDrivenPropertyChecks also take a variable length argument list of PropertyCheckConfigParam + objects that you can use to override the values provided by the implicit PropertyCheckConfiguration for a single forAll + invocation. For example, if you want to set minSuccessful to 500 for just one particular forAll invocation, + you can do so like this: +

+ +
+forAll (minSuccessful(500)) { (n: Int, d: Int) => ...
+
+ +

+ This invocation of forAll will use 500 for minSuccessful and whatever values are specified by the + implicitly passed PropertyCheckConfiguration object for the other configuration parameters. + If you want to set multiple configuration parameters in this way, just list them separated by commas: +

+ +
+forAll (minSuccessful(500), maxDiscardedFactor(30)) { (n: Int, d: Int) => ...
+
+ +

+ If you are using an overloaded form of forAll that already takes an initial parameter list, just + add the configuration parameters after the list of generators, names, or generator/name pairs, as in: +

+ +
+// If providing argument names
+forAll ("n", "d", minSuccessful(500), maxDiscardedFactor(30)) {
+  (n: Int, d: Int) => ...
+
// If providing generators +forAll (validNumers, validDenoms, minSuccessful(500), maxDiscardedFactor(30)) { + (n: Int, d: Int) => ... +
// If providing (<generators>, <name>) pairs +forAll ((validNumers, "n"), (validDenoms, "d"), minSuccessful(500), maxDiscardedFactor(30)) { + (n: Int, d: Int) => ... +
+ +

+ For more information, see the documentation for trait Configuration, + a supertrait of GeneratorDrivenPropertyChecks. +

+ +

+ Next, learn about ScalaTest's philosophy and design. +

+ +
+} diff --git a/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html b/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html new file mode 100644 index 0000000000..de560acdc8 --- /dev/null +++ b/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html @@ -0,0 +1,177 @@ +@* +* Copyright 2010-2017 Artima, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*@ + +@import controllers.Application._ + +@userGuidePage("Property-based testing") { +
+ +

Property-based testing using ScalaCheck

+ +

+ ScalaTest supports property-based testing, where + a property is a high-level specification of behavior that should hold for a range of data points. For example, a property might + state that the size of a list returned from a method should always be greater than or equal to the size of the list passed to + that method. This property should hold no matter what list is passed. +

+ +

+ The difference between a traditional test and a property is that tests traditionally verify behavior based on specific data points checked + by the test. A test might pass three or four specific lists of different sizes to a method under test that takes a list, for example, and check the results + are as expected. A property, by contrast, would describe at a high level the preconditions of the method under test and specify some aspect of the + result that should hold no matter what valid list is passed. +

+ +

+ In ScalaTest, properties are specified as functions and the data points used to check properties can be supplied by either tables + or generators. Generator-driven property checks are performed via integration with ScalaCheck through + ScalaTest + ScalaCheck library, by including: +

+ +
+libraryDependencies += "org.scalatestplus" %% "scalacheck-@{latestScalaCheckVersion}" % "@{latestScalaCheckPlusVersion}" % "test"
+
+ +

+ in your SBT file, or if you use Maven: +

+ +
+<dependency>
+    <groupId>org.scalatestplus</groupId>
+    <artifactId>scalacheck-@{latestScalaCheckVersion}_2.13</artifactId>
+    <version>@latestScalaCheckPlusVersion</version>
+    <scope>test</scope>
+</dependency>
+
+ +

+ To use this style of testing, mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks (previously known as org.scalatest.props.PropertyChecks, deprecated in 3.0.6), or import the + members of its companion object. +

+ +

+ As an example property-based testing using both table- and generator-driven properties, imagine you want to test this Fraction class: +

+ +
+class Fraction(n: Int, d: Int) {
+
require(d != 0) + require(d != Integer.MIN_VALUE) + require(n != Integer.MIN_VALUE) +
val numer = if (d < 0) -1 * n else n + val denom = d.abs +
override def toString = numer + " / " + denom +} +
+ +

+ If you mix in ScalaCheckPropertyChecks, you could use a generator-driven property check to test that the passed values for numerator and + denominator are properly normalized, like this: +

+ +
+forAll { (n: Int, d: Int) =>
+
whenever (d != 0 && d != Integer.MIN_VALUE + && n != Integer.MIN_VALUE) { +
val f = new Fraction(n, d) +
if (n < 0 && d < 0 || n > 0 && d > 0) + f.numer should be > 0 + else if (n != 0) + f.numer should be < 0 + else + f.numer should be === 0 +
f.denom should be > 0 + } +} +
+ +

+ The forAll method takes a function that expresses a property of Fraction that should hold for any valid values + passed as n and d. (This property specifies how the numerator and denominator passed to the Fraction + constructor should be normalized.) The whenever clause indicates invalid + values for n and d. + Any other values are valid. When run, ScalaCheck generators will be passed implicitly to forAll and supply integer values for + n and d. By default the property will be checked against 10 valid pairs of n and d. Note that this is different from ScalaCheck's default of 100. +

+ +

+ You might place the previous property check in its own test whose name describes the property at a high level, such as + "Fractions should be normalized". + In another test you might use a table-driven property check to test that all combinations of invalid values passed to the Fraction constructor + produce the expected IllegalArgumentException, like this: +

+ +
+val invalidCombos =
+  Table(
+    ("n",               "d"),
+    (Integer.MIN_VALUE, Integer.MIN_VALUE),
+    (1,                 Integer.MIN_VALUE),
+    (Integer.MIN_VALUE, 1),
+    (Integer.MIN_VALUE, 0),
+    (1,                 0)
+  )
+
forAll (invalidCombos) { (n: Int, d: Int) => + evaluating { + new Fraction(n, d) + } should produce [IllegalArgumentException] +} +
+ +

+ In this example, invalidCombos is a table of two columns, produced by the Table factory method, which takes + a comma-separated list of tuples. The first tuple contains the headings (the names of the columns, "n" and "d"). The remaining + tuples represent the rows of data. In this example, each row is one example of how invalid data that could be passed to the Fraction + constructor. +

+ +

+ After the declaration of the table, forAll is invoked. This forAll method takes the table, invalidCombos, and + a property. forAll checks to make sure the property holds for each row of the table. +

+ +

+ When you get a failed property check, ScalaTest will report the failure with initial seed so that you can reproduce the failure. You'll need to pass the initial seed value through -S argument, + here's an example you pass it through build.sbt: +

+ +
+testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-S", "123456789")
+
+ + or if you are using testOnly you can do: + +
+sbt> testOnly MyTest -- -S 123456879
+
+ +

+ For more information check out the user guide pages for: +

+ + + +

+ Next, learn about Asynchronous testing. +

+ +
+} diff --git a/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html b/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html new file mode 100644 index 0000000000..960e38a00f --- /dev/null +++ b/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html @@ -0,0 +1,283 @@ +@* +* Copyright 2010-2017 Artima, Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*@ + +@import controllers.Application.{scaladocsPageUrl, plusScalaCheck16ScaladocsPageUrl} + +@userGuidePage("Table-driven property checks") { +
+ +

Table-driven property checks

+ +

+ To use table-driven property checks, you must mix in trait TableDrivenPropertyChecks (or import the + members of its companion object). If you are also using ScalaCheck generator-driven property checks, you can mix in + trait ScalaCheckPropertyChecks, + which extends both TableDrivenPropertyChecks and ScalaCheckDrivenPropertyChecks. +

+ +

+ Trait TableDrivenPropertyChecks contains one forAll method for each TableForN + class, TableFor1 + through TableFor22, which allow properties to be checked against the + rows of a table. It also contains a wherever method that can be used to indicate a property need only hold whenever some + condition is true. +

+ +

+ For an example of trait TableDrivenPropertyChecks in action, imagine you want to test this Fraction class: +

+ +
+class Fraction(n: Int, d: Int) {
+
require(d != 0) + require(d != Integer.MIN_VALUE) + require(n != Integer.MIN_VALUE) +
val numer = if (d < 0) -1 * n else n + val denom = d.abs +
override def toString = numer + " / " + denom +} +
+ +

+ TableDrivenPropertyChecks allows you to create tables with + between 1 and 22 columns and any number of rows. You create a table by passing + tuples to one of the factory methods of object Table. Each tuple must have the + same arity (number of members). The first tuple you pass must all be strings, because + it define names for the columns. Subsequent tuples define the data. After the initial tuple + that contains string column names, all tuples must have the same type. For example, + if the first tuple after the column names contains two Ints, all subsequent + tuples must contain two Int (i.e., have type + Tuple2[Int, Int]). +

+ +

+ To test the behavior of Fraction, you could create a table + of numerators and denominators to pass to the constructor of the + Fraction class using one of the apply factory methods declared + in Table, like this: +

+ +
+import org.scalatest.prop.TableDrivenPropertyChecks._
+
val fractions = + Table( + ("n", "d"), // First tuple defines column names + ( 1, 2), // Subsequent tuples define the data + ( -1, 2), + ( 1, -2), + ( -1, -2), + ( 3, 1), + ( -3, 1), + ( -3, 0), + ( 3, -1), + ( 3, Integer.MIN_VALUE), + (Integer.MIN_VALUE, 3), + ( -3, -1) + ) +
+ +

+ You could then check a property against each row of the table using a forAll method, like this: +

+ +
+import org.scalatest.matchers.should.Matchers._
+
forAll (fractions) { (n: Int, d: Int) => +
whenever (d != 0 && d != Integer.MIN_VALUE + && n != Integer.MIN_VALUE) { +
val f = new Fraction(n, d) +
if (n < 0 && d < 0 || n > 0 && d > 0) + f.numer should be > 0 + else if (n != 0) + f.numer should be < 0 + else + f.numer should be === 0 +
f.denom should be > 0 + } +} +
+ +

+ Trait TableDrivenPropertyChecks provides 22 overloaded forAll methods + that allow you to check properties using the data provided by a table. Each forAll + method takes two parameter lists. The first parameter list is a table. The second parameter list + is a function whose argument types and number matches that of the tuples in the table. For + example, if the tuples in the table supplied to forAll each contain an + Int, a String, and a List[Char], then the function supplied + to forAll must take 3 parameters, an Int, a String, + and a List[Char]. The forAll method will pass each row of data to + the function, and generate a TableDrivenPropertyCheckFailedException if the function + completes abruptly for any row of data with any exception that would normally cause a test to + fail in ScalaTest other than DiscardedEvaluationException. An + DiscardedEvaluationException, + which is thrown by the whenever method (also defined in this trait) to indicate + a condition required by the property function is not met by a row + of passed data, will simply cause forAll to skip that row of data. +

+ +

Testing stateful functions

+ +

+ One way to use a table with one column is to test subsequent return values + of a stateful function. Imagine, for example, you had an object named FiboGen + whose next method returned the next fibonacci number, where next + means the next number in the series following the number previously returned by next. + So the first time next was called, it would return 0. The next time it was called + it would return 1. Then 1. Then 2. Then 3, and so on. FiboGen would need to + maintain state, because it has to remember where it is in the series. In such a situation, + you could create a TableFor1 (a table with one column, which you could alternatively + think of as one row), in which each row represents + the next value you expect. +

+ +
+val first14FiboNums =
+  Table("n", 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233)
+
+ +

+ Then in your forAll simply call the function and compare it with the + expected return value, like this: +

+ +
+forAll (first14FiboNums) { n =>
+  FiboGen.next should equal (n)
+}
+
+ +

Testing mutable objects

+ +

+ If you need to test a mutable object, one way you can use tables is to specify + state transitions in a table. For example, imagine you wanted to test this mutable + Counter class: + +

+class Counter {
+  private var c = 0
+  def reset() { c = 0 }
+  def click() { c += 1 }
+  def enter(n: Int) { c = n }
+  def count = c
+}
+
+ +

+ A Counter keeps track of how many times its click method + is called. The count starts out at zero and increments with each click + invocation. You can also set the count to a specific value by calling enter + and passing the value in. And the reset method returns the count back to + zero. You could define the actions that initiate state transitions with case classes, like this: +

+ +
+abstract class Action
+case object Start extends Action
+case object Click extends Action
+case class Enter(n: Int) extends Action
+
+ +

+ Given these actions, you could define a state-transition table like this: +

+ +
+val stateTransitions =
+  Table(
+    ("action", "expectedCount"),
+    (Start,    0),
+    (Click,    1),
+    (Click,    2),
+    (Click,    3),
+    (Enter(5), 5),
+    (Click,    6),
+    (Enter(1), 1),
+    (Click,    2),
+    (Click,    3)
+  )
+
+ +

+ To use this in a test, simply do a pattern match inside the function you pass + to forAll. Make a pattern for each action, and have the body perform that + action when there's a match. Then check that the actual value equals the expected value: +

+ +
+val counter = new Counter
+forAll (stateTransitions) { (action, expectedCount) =>
+  action match {
+    case Start => counter.reset()
+    case Click => counter.click()
+    case Enter(n) => counter.enter(n)
+  }
+  counter.count should equal (expectedCount)
+}
+
+ +

Testing invalid argument combinations

+ +

+ A table-driven property check can also be helpful to ensure that the proper exception is thrown when invalid data is + passed to a method or constructor. For example, the Fraction constructor shown above should throw IllegalArgumentException + if Integer.MIN_VALUE is passed for either the numerator or denominator, or zero is passed for the denominator. This yields the + following five combinations of invalid data: +

+ + + + + + + + +
nd
Integer.MIN_VALUEInteger.MIN_VALUE
a valid valueInteger.MIN_VALUE
Integer.MIN_VALUEa valid value
Integer.MIN_VALUEzero
a valid valuezero
+ +

+ You can express these combinations in a table: +

+ +
+val invalidCombos =
+  Table(
+    ("n",               "d"),
+    (Integer.MIN_VALUE, Integer.MIN_VALUE),
+    (1,                 Integer.MIN_VALUE),
+    (Integer.MIN_VALUE, 1),
+    (Integer.MIN_VALUE, 0),
+    (1,                 0)
+  )
+
+ +

+ Given this table, you could check that all invalid combinations produce IllegalArgumentException, like this: +

+ +
+forAll (invalidCombos) { (n: Int, d: Int) =>
+  an [IllegalArgumentException] should be thrownBy {
+    new Fraction(n, d)
+  }
+}
+
+ +

+ Next, learn about generator-driven property checks. +

+ +
+} diff --git a/app/views/userGuide/tableDrivenPropertyChecks.scala.html b/app/views/userGuide/tableDrivenPropertyChecks.scala.html index dd325d1e2d..ebc5102af0 100644 --- a/app/views/userGuide/tableDrivenPropertyChecks.scala.html +++ b/app/views/userGuide/tableDrivenPropertyChecks.scala.html @@ -23,9 +23,9 @@

Table-driven property checks

To use table-driven property checks, you must mix in trait TableDrivenPropertyChecks (or import the -members of its companion object). If you are also using ScalaCheck generator-driven property checks, you can mix in -trait ScalaCheckPropertyChecks, -which extends both TableDrivenPropertyChecks and ScalaCheckDrivenPropertyChecks. +members of its companion object). If you are also using ScalaTest generator-driven property checks, you can mix in +trait PropertyChecks, +which extends both TableDrivenPropertyChecks and GeneratorDrivenPropertyChecks.

diff --git a/conf/routes b/conf/routes index b3a1552fd6..3932842589 100644 --- a/conf/routes +++ b/conf/routes @@ -97,6 +97,7 @@ GET /user_guide/using_matchers controll GET /user_guide/testing_with_mock_objects controllers.UserGuide.testingWithMockObjects GET /user_guide/tests_as_specifications controllers.UserGuide.testsAsSpecifications GET /user_guide/property_based_testing controllers.UserGuide.propertyBasedTesting() +GET /user_guide/scalacheck_property_based_testing controllers.UserGuide.scalacheckPropertyBasedTesting() GET /user_guide/other_goodies controllers.UserGuide.otherGoodies GET /user_guide/using_inside controllers.UserGuide.usingInside GET /user_guide/using_OptionValues controllers.UserGuide.usingOptionValues @@ -110,7 +111,9 @@ GET /user_guide/selecting_a_style controll GET /user_guide/defining_base_classes controllers.UserGuide.definingBaseClasses() GET /user_guide/using_junit_runner controllers.UserGuide.usingJunitRunner GET /user_guide/table_driven_property_checks controllers.UserGuide.tableDrivenPropertyChecks +GET /user_guide/scalacheck_table_driven_property_checks controllers.UserGuide.scalacheckTableDrivenPropertyChecks GET /user_guide/generator_driven_property_checks controllers.UserGuide.generatorDrivenPropertyChecks +GET /user_guide/scalacheck_driven_property_checks controllers.UserGuide.scalacheckDrivenPropertyChecks GET /user_guide/writing_scalacheck_style_properties controllers.UserGuide.writingScalacheckStyleProperties GET /user_guide/using_the_runner controllers.UserGuide.usingTheRunner GET /user_guide/invoking_execute controllers.UserGuide.invokingExecute From 963d89b048821c0c13854d73fa9b6464d27d1cc5 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Thu, 31 Aug 2023 17:43:50 +0800 Subject: [PATCH 4/8] Adjusted documentations for property checks using ScalaCheck. --- .../scalacheckDrivenPropertyChecks.scala.html | 16 ++++++++-------- .../scalacheckPropertyBasedTesting.scala.html | 8 ++++---- ...calacheckTableDrivenPropertyChecks.scala.html | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html b/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html index 13ec3dff6a..e12fbe41b8 100644 --- a/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html +++ b/app/views/userGuide/scalacheckDrivenPropertyChecks.scala.html @@ -19,11 +19,11 @@ @userGuidePage("Generator-driven property checks") {

-

ScalaCheck-driven property checks

+

Generator-driven property checks using ScalaCheck

- To use generator-driven property checks, you must mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks - (previously known as org.scalatest.props.PropertyChecks). If you are also using table-driven property checks, you can mix in + To use generator-driven property checks, you must mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks. + If you are also using table-driven property checks, you can mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks, which extends both ScalaCheckDrivenPropertyChecks and TableDrivenPropertyChecks. @@ -339,11 +339,11 @@

ScalaCheck-driven property checks

- The forAll methods of trait GeneratorDrivenPropertyChecks each take a PropertyCheckConfiguration + The forAll methods of trait ScalaCheckDrivenPropertyChecks each take a PropertyCheckConfiguration object as an implicit parameter. This object provides values for each of the five configuration parameters. Trait Configuration provides an implicit val named generatorDrivenConfig with each configuration parameter set to its default value. If you want to set one or more configuration parameters to a different value for all property checks in a suite you can override this - val (or hide it, for example, if you are importing the members of the GeneratorDrivenPropertyChecks companion object rather + val (or hide it, for example, if you are importing the members of the ScalaCheckDrivenPropertyChecks companion object rather than mixing in the trait.) For example, if you want all parameters at their defaults except for minSize and maxSize, you can override generatorDrivenConfig, like this: @@ -364,7 +364,7 @@

ScalaCheck-driven property checks

In addition to taking a PropertyCheckConfiguration object as an implicit parameter, the forAll methods of trait - GeneratorDrivenPropertyChecks also take a variable length argument list of PropertyCheckConfigParam + ScalaCheckDrivenPropertyChecks also take a variable length argument list of PropertyCheckConfigParam objects that you can use to override the values provided by the implicit PropertyCheckConfiguration for a single forAll invocation. For example, if you want to set minSuccessful to 500 for just one particular forAll invocation, you can do so like this: @@ -403,11 +403,11 @@

ScalaCheck-driven property checks

For more information, see the documentation for trait Configuration, - a supertrait of GeneratorDrivenPropertyChecks. + a supertrait of ScalaCheckDrivenPropertyChecks.

- Next, learn about ScalaTest's philosophy and design. + If you want to do table driven property check using ScalaCheck, learn about Table-driven property checks using ScalaCheck.

diff --git a/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html b/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html index de560acdc8..01b825bc0b 100644 --- a/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html +++ b/app/views/userGuide/scalacheckPropertyBasedTesting.scala.html @@ -22,7 +22,7 @@

Property-based testing using ScalaCheck

- ScalaTest supports property-based testing, where + ScalaTest supports property-based testing using ScalaCheck, where a property is a high-level specification of behavior that should hold for a range of data points. For example, a property might state that the size of a list returned from a method should always be greater than or equal to the size of the list passed to that method. This property should hold no matter what list is passed. @@ -59,7 +59,7 @@

Property-based testing using ScalaCheck

- To use this style of testing, mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks (previously known as org.scalatest.props.PropertyChecks, deprecated in 3.0.6), or import the + To use this style of testing, mix in trait org.scalatestplus.scalacheck.ScalaCheckPropertyChecks, or import the members of its companion object.

@@ -164,8 +164,8 @@

Property-based testing using ScalaCheck

diff --git a/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html b/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html index 960e38a00f..3f9453e3ef 100644 --- a/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html +++ b/app/views/userGuide/scalacheckTableDrivenPropertyChecks.scala.html @@ -19,7 +19,7 @@ @userGuidePage("Table-driven property checks") {
-

Table-driven property checks

+

Table-driven property checks using ScalaCheck

To use table-driven property checks, you must mix in trait TableDrivenPropertyChecks (or import the @@ -276,7 +276,7 @@

Table-driven property checks

- Next, learn about generator-driven property checks. + If you want to do ScalaCheck generator driven property check, learn about Generator-driven property checks using ScalaCheck.

From b33ff30154de91059b926861ab4fe1a2643e87b4 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Thu, 4 Jul 2024 14:29:48 +0800 Subject: [PATCH 5/8] Re-enabled back the using Expectations guide. --- app/views/sidenav.scala.html | 2 +- app/views/userGuide/usingAssertions.scala.html | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/views/sidenav.scala.html b/app/views/sidenav.scala.html index b352688da0..d27669cbd0 100644 --- a/app/views/sidenav.scala.html +++ b/app/views/sidenav.scala.html @@ -32,7 +32,7 @@ @genlink("Defining base classes", "/user_guide/defining_base_classes") @genlink("Writing your first test", routes.UserGuide.writingYourFirstTest().url) @genlink("Using assertions", routes.UserGuide.usingAssertions().url) -@* @genlink("Using expectations", routes.UserGuide.usingExpectations().url) *@ +@genlink("Using expectations", routes.UserGuide.usingExpectations().url) @genlink("Tagging your tests", routes.UserGuide.taggingYourTests().url) @genlink("Running your tests", "/user_guide/running_your_tests") @genlink("Sharing fixtures", "/user_guide/sharing_fixtures") diff --git a/app/views/userGuide/usingAssertions.scala.html b/app/views/userGuide/usingAssertions.scala.html index 43fe401aca..69ffa24ec8 100644 --- a/app/views/userGuide/usingAssertions.scala.html +++ b/app/views/userGuide/usingAssertions.scala.html @@ -242,14 +242,8 @@

The assert macro

If you wish to place a clue string after a block of code, see the documentation for AppendedClues.

-@* -

-Next, learn about Using expectations. -

-*@ -

-Next, learn about tagging your tests. +Next, learn about using expectations.

From acfcf119c10222b4ab8cec53a5eb9b426624bf46 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Thu, 4 Jul 2024 14:55:27 +0800 Subject: [PATCH 6/8] Updated latest version to 3.3.0. --- app/controllers/Application.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala index a223140eb9..eaf91e920c 100644 --- a/app/controllers/Application.scala +++ b/app/controllers/Application.scala @@ -139,7 +139,7 @@ class Application @Inject() (cc: ControllerComponents) extends AbstractControlle object Application { - val latestVersion = "3.2.19" + val latestVersion = "3.3.0" val latestSuperSafeVersion = "1.1.12" val supersafeScalaVersion = "2.13.14" val milestoneVersion = "3.2.0" @@ -159,13 +159,13 @@ object Application { val latestJUnit5Version = "5-10" val latestMockitoVersion = "5-12" val latestScalaCheckVersion = "1-18" - val latestScalaCheckPlusVersion = "3.2.19.0" + val latestScalaCheckPlusVersion = "3.3.0.0" val latestTestNGVersion = "7-10" val quickStartXmlJar = "https://repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_3/2.3.0/scala-xml_3-2.3.0.jar" - val latestPlusScalaCheckDoc = "plus-scalacheck-1.18/3.2.19.0" - val latestPlusEasyMockDoc = "plus-easymock-5.3/3.2.19.0" - val latestPlusJMockDoc = "plus-jmock-2.13/3.2.19.0" - val latestPlusMockitoDoc = "plus-mockito-5.12/3.2.19.0" + val latestPlusScalaCheckDoc = "plus-scalacheck-1.18/3.3.0.0" + val latestPlusEasyMockDoc = "plus-easymock-5.3/3.3.0.0" + val latestPlusJMockDoc = "plus-jmock-2.13/3.3.0.0" + val latestPlusMockitoDoc = "plus-mockito-5.12/3.3.0.0" def scaladocsPageUrl(file: String, version: String = latestVersion): String = { val oldScaladocStyle30Releases = List("3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4") From 955ec710911e024b71026569efd6e37e08c2e8aa Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Thu, 4 Jul 2024 22:34:50 +0800 Subject: [PATCH 7/8] Added release note for 3.3.0. --- app/controllers/ReleaseNotes.scala | 3 + app/views/homePage.scala.html | 2 +- app/views/plus/easymockVersions.scala.html | 6 + app/views/plus/jmockVersions.scala.html | 6 + app/views/plus/junit4Versions.scala.html | 6 + app/views/plus/junit5Versions.scala.html | 6 + app/views/plus/mockitoVersions.scala.html | 6 + app/views/plus/scalacheckVersions.scala.html | 5 + app/views/plus/seleniumVersions.scala.html | 6 + app/views/plus/testngVersions.scala.html | 6 + app/views/quickStart.scala.html | 7 +- app/views/releaseNotes/v330.scala.html | 144 +++++++++++++++++++ conf/routes | 1 + 13 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 app/views/releaseNotes/v330.scala.html diff --git a/app/controllers/ReleaseNotes.scala b/app/controllers/ReleaseNotes.scala index 7c6204266a..ff70a76c70 100644 --- a/app/controllers/ReleaseNotes.scala +++ b/app/controllers/ReleaseNotes.scala @@ -25,6 +25,9 @@ class ReleaseNotes @Inject() (cc: ControllerComponents) extends AbstractControll Ok(views.html.releaseNotes.releaseNotesIndex()) } + def v330 = Action { + Ok(views.html.releaseNotes.v330()) + } def v3219 = Action { Ok(views.html.releaseNotes.v3219()) } diff --git a/app/views/homePage.scala.html b/app/views/homePage.scala.html index 5a108a585a..c79cebb654 100644 --- a/app/views/homePage.scala.html +++ b/app/views/homePage.scala.html @@ -25,7 +25,7 @@ height="200" alt="ScalaTest: Simply Productive"/>

-Latest Release - ScalaTest and Scalactic @latestVersion! +Latest Release - ScalaTest and Scalactic @latestVersion!

} diff --git a/app/views/plus/easymockVersions.scala.html b/app/views/plus/easymockVersions.scala.html index a4d024f7fc..ef44e2ebf5 100644 --- a/app/views/plus/easymockVersions.scala.html +++ b/app/views/plus/easymockVersions.scala.html @@ -31,6 +31,12 @@

ScalaTest + EasyMock Versions, Versions, Versions

+ + + + + + diff --git a/app/views/plus/jmockVersions.scala.html b/app/views/plus/jmockVersions.scala.html index d77648f684..f57d77dc1c 100644 --- a/app/views/plus/jmockVersions.scala.html +++ b/app/views/plus/jmockVersions.scala.html @@ -31,6 +31,12 @@

ScalaTest + JMock Versions, Versions, Versions

ScalaTest + EasyMock VersionScalaTest VersionEasyMock Versions
3.3.0.03.3.05.3
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/plus/junit4Versions.scala.html b/app/views/plus/junit4Versions.scala.html index dc44a2c3ad..e02f3b7989 100644 --- a/app/views/plus/junit4Versions.scala.html +++ b/app/views/plus/junit4Versions.scala.html @@ -31,6 +31,12 @@

ScalaTest + JUnit 4 Versions, Versions, Versions

ScalaTest + JMock VersionScalaTest VersionJMock Versions
3.3.0.03.3.02.13.x
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/plus/junit5Versions.scala.html b/app/views/plus/junit5Versions.scala.html index ef14c844be..9734d337f5 100644 --- a/app/views/plus/junit5Versions.scala.html +++ b/app/views/plus/junit5Versions.scala.html @@ -31,6 +31,12 @@

ScalaTest + JUnit 5 Versions, Versions, Versions

ScalaTest + JUnit 4 VersionScalaTest VersionJUnit 4 Versions
3.3.0.03.3.04.13
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/plus/mockitoVersions.scala.html b/app/views/plus/mockitoVersions.scala.html index 10b224edd8..bf9e1617c3 100644 --- a/app/views/plus/mockitoVersions.scala.html +++ b/app/views/plus/mockitoVersions.scala.html @@ -31,6 +31,12 @@

ScalaTest + Mockito Versions, Versions, Versions

ScalaTest + JUnit 5 VersionScalaTest VersionJUnit 5 Versions
3.3.0.03.3.05.10
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/plus/scalacheckVersions.scala.html b/app/views/plus/scalacheckVersions.scala.html index ffa709b6b2..70c5e22841 100644 --- a/app/views/plus/scalacheckVersions.scala.html +++ b/app/views/plus/scalacheckVersions.scala.html @@ -31,6 +31,11 @@

ScalaTest + ScalaCheck Versions, Versions, Versions

ScalaTest + Mockito VersionScalaTest VersionMockito Versions
3.3.0.03.3.05.12
3.2.19.0 3.2.19
+ + + + +/tr> diff --git a/app/views/plus/seleniumVersions.scala.html b/app/views/plus/seleniumVersions.scala.html index 4eb63b7b86..d4edff8ae9 100644 --- a/app/views/plus/seleniumVersions.scala.html +++ b/app/views/plus/seleniumVersions.scala.html @@ -31,6 +31,12 @@

ScalaTest + Selenium Versions, Versions, Versions

ScalaTest + ScalaCheck VersionScalaTest VersionScalaCheck Versions
3.3.0.03.3.01.18.x
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/plus/testngVersions.scala.html b/app/views/plus/testngVersions.scala.html index 83afc6af13..75040d773d 100644 --- a/app/views/plus/testngVersions.scala.html +++ b/app/views/plus/testngVersions.scala.html @@ -31,6 +31,12 @@

ScalaTest + TestNG Versions, Versions, Versions

ScalaTest + Selenium VersionScalaTest VersionSelenium Versions
3.3.0.03.3.04.21
3.2.19.0 3.2.19
+ + + + + + diff --git a/app/views/quickStart.scala.html b/app/views/quickStart.scala.html index 2044c590ba..043a3ce917 100644 --- a/app/views/quickStart.scala.html +++ b/app/views/quickStart.scala.html @@ -69,14 +69,11 @@

ScalaTest quick start

-To run it, you will need one more artifact, -the Jar file for Scala's XML module. -Once you've downloaded that Jar file, you can run ExampleSpec like this: +You can run ExampleSpec like this:

-$ CLASSPATH=scalatest-app_@{majorMinorScalaVersion}-@{latestVersion}.jar:scala-xml_3-2.3.0.jar
-$ scala -cp $CLASSPATH org.scalatest.run ExampleSpec
+$ scala -cp scalatest-app_@{majorMinorScalaVersion}-@{latestVersion}.jar org.scalatest.run ExampleSpec
 Run starting. Expected test count is: 2
 ExampleSpec:
 A Stack
diff --git a/app/views/releaseNotes/v330.scala.html b/app/views/releaseNotes/v330.scala.html
new file mode 100644
index 0000000000..85709bb133
--- /dev/null
+++ b/app/views/releaseNotes/v330.scala.html
@@ -0,0 +1,144 @@
+@*
+* Copyright 2010-2024 Artima, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*@
+
+@import controllers.Application.scaladocsPageUrl
+@import controllers.Application.milestoneScaladoc
+
+@combinedReleaseNotesPage("Scalactic/ScalaTest 3.3.0 Release Notes") {
+
+ +

ScalaTest/Scalactic 3.3.0 Release Notes

+ +

+ ScalaTest/Scalactic 3.3.0 (for Scala 2.10, 2.11, 2.12, 2.13, 3.1; on the JVM, JavaScript, and native) + includes the enhancements listed below. +

+ +

+ For information on how to include ScalaTest in your project, see the install page. + For information on how to use Scalactic in your production code, + see its install page. +

+ +
+ + + +
+ +
+

ScalaTest

+
+ +
+ +

+ ScalaTest 3.3.0 is a major release of ScalaTest that makes new features and improvements, and supports Scala.js 1.16.x, Scala.native 0.5.x and Scala 3.3.x. +

+ + +

New Features

+

ScalaTest Generators.

+

Pure style traits. (PR #2011)

+

Expectations. (PR #2244)

+

Safe PrivateMethodTester. (PR #2301)

+ + +

Improvements

+

Switched from flexmark to commonmark in HtmlReporter. (PR #2136)

+

Escaping String Differ. (PR #2327)

+

Expose discovered suites with Runner. (PR #2319)

+

Added checkpoint method to Checkpoints trait. (PR #2305)

+

Set sorting reporter as complete if test run failed unexpectedly (PR #2161)

+

ScalaTest Runner can run now without scala-xml dependency.

+

Added withRetries to Retries. (PR #2282)

+

Changed TaskRunner in Scala-js build to use Macrotask executor. (PR #2278)

+

Enhanced HtmlReporter to build summary based on suite files. (PR #2275)

+

Updated to SBT 1.9.6.

+

Updated to Ant 1.10.14.

+

Updated scala versions for jvm, js and native to newer version.

+ + +

Breaking Changes

+

Dropped TestRegistration, FixtureTestRegistratio, AsyncTestRegistration and FixtureAsyncTestRegistration. (PR #2109)

+

Changed Valuable class in EitherValues to EitherValuable, and in OptionValues to OptionValuable. (PR #2085)

+

Dropped support for Scala 2.10, code that cater for Scala 2.10 are removed, e.g. we can now use ClassTag's unapply directly in TypeMatcherHelper. (PR #2052)

+

Dropped support for Scala-js 0.6. (PR #2052)

+

Removed deprecated functions in DiagramsHelper. (PR #2047)

+

Removed optional dependency to Guice.

+

Removed optional dependency to ASM.

+ +
+ + + +
+ +
+

Scalactic

+
+ +
+ + +

Bug Fixes

+

Scalactic 3.3.0 contains no new features.

+ + + +

Improvements

+

Updated to SBT 1.9.6.

+

Updated scala versions for jvm, js and native to newer version.

+ + +

Breaking Changes

+

Dropped support for Scala 2.10, code that cater for Scala 2.10 are removed, e.g. we can now use ClassTag's unapply directly in TypeMatcherHelper. (PR #2052)

+

Dropped support for Scala-js 0.6. (PR #2052)

+ +
+ +
+
+ +

ScalaTest/Scalactic Contributors

+ + Thanks to the contributors for the ScalaTest/Scalactic 3.3.0 release (retrieved using: git shortlog -sne release-3.2.19..release-3.3.0): + + + +
+ + + +
+ +

+ Visit ScalaTest Release Notes for links to the release notes of all previous versions, or + step back in time by visiting the release notes for the previous version. +

+ +
+} diff --git a/conf/routes b/conf/routes index dbf9dda6ac..875b9c6c93 100644 --- a/conf/routes +++ b/conf/routes @@ -28,6 +28,7 @@ GET /getting_started_with_testng_in_scala controll GET /at_a_glance/:style controllers.Application.atAGlance(style) GET /at_a_glance controllers.Application.bareAtAGlance() +GET /release_notes/3.3.0 controllers.ReleaseNotes.v330 GET /release_notes/3.2.19 controllers.ReleaseNotes.v3219 GET /release_notes/3.2.18 controllers.ReleaseNotes.v3218 GET /release_notes/3.2.17 controllers.ReleaseNotes.v3217 From 88ba59a002f223399a2490e04adcfcd8e2988c75 Mon Sep 17 00:00:00 2001 From: Chua Chee Seng Date: Wed, 11 Sep 2024 15:02:15 +0800 Subject: [PATCH 8/8] Enhanced ScalaTest Generators guide to include custom generators, combine generator and some text about shrinking. --- .../userGuide/propertyBasedTesting.scala.html | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/views/userGuide/propertyBasedTesting.scala.html b/app/views/userGuide/propertyBasedTesting.scala.html index ff8fcba7e2..f0620b95d2 100644 --- a/app/views/userGuide/propertyBasedTesting.scala.html +++ b/app/views/userGuide/propertyBasedTesting.scala.html @@ -127,6 +127,31 @@

Property-based testing

a property. forAll checks to make sure the property holds for each row of the table.

+

Custom Generators

+ +

+You can define your own generators to suit specific testing needs: +

+ +
+val evenIntGen = Generator[Int] = for {
+  n <- Generator.choose(0, 100)
+} yield n * 2
+
+ +

+You can also combine multiple generators to create more complex types of data: +

+ +
+val personGen: Generator[Person] = for {
+  name <- Generator.alphaStr
+  age  <- Generator.choose(18, 120)
+} yield Person(name, age)
+
+ +

Shrinking Failing Value

+

When you get a failed property check, ScalaTest will report the failure with initial seed so that you can reproduce the failure. You'll need to pass the initial seed value through -S argument, here's an example you pass it through build.sbt: @@ -142,6 +167,10 @@

Property-based testing

sbt> testOnly MyTest -- -S 123456879
+

+When a test fails, ScalaTest automatically tries to simplify (or “shrink”) the value to help you identify the root cause of the failure. This feature is invaluable for debugging. +

+

For more information check out the user guide pages for:

ScalaTest + TestNG VersionScalaTest VersionTestNG Versions
3.3.0.03.3.07.10
3.2.19.0 3.2.19