From 8f23fd19f36d5b68494141eaea6f3261b0d8dbf7 Mon Sep 17 00:00:00 2001 From: etorreborre Date: Fri, 10 Sep 2021 23:37:39 +0200 Subject: [PATCH] doc: reworked the indentation on the user guide --- .scalafmt.conf | 2 + .../specs2/specification/SnippetsSpec.scala | 18 +- .../scala/org/specs2/guide/AddKeywords.scala | 72 +++--- .../org/specs2/guide/ArgumentsReference.scala | 10 +- .../org/specs2/guide/AsResultTypeclass.scala | 39 ++-- .../scala/org/specs2/guide/AutoExamples.scala | 49 ++-- .../org/specs2/guide/CaptureSnippets.scala | 10 +- .../scala/org/specs2/guide/Contexts.scala | 161 +++++++------- .../guide/CreateOnlineSpecifications.scala | 58 +++-- .../scala/org/specs2/guide/Environment.scala | 43 ++-- .../org/specs2/guide/ExampleDescription.scala | 109 +++++---- .../scala/org/specs2/guide/Execution.scala | 32 +-- .../specs2/guide/ExecutionEnvironments.scala | 47 ++-- .../specs2/guide/ExpectationDescription.scala | 34 +-- .../scala/org/specs2/guide/ForLoops.scala | 111 +++++---- .../scala/org/specs2/guide/FragmentsApi.scala | 27 ++- .../org/specs2/guide/GetAllExpectations.scala | 32 +-- .../test/scala/org/specs2/guide/HowTo.scala | 13 +- .../scala/org/specs2/guide/HtmlOutput.scala | 33 +-- .../org/specs2/guide/LightweightSpecs.scala | 48 ++-- .../scala/org/specs2/guide/Matchers.scala | 149 ++++++------- .../specs2/guide/MultilineDescriptions.scala | 32 ++- .../org/specs2/guide/MutableSpecSyntax.scala | 60 +++-- .../org/specs2/guide/OtherBuildTools.scala | 14 +- .../guide/PendingUntilFixedExamples.scala | 25 ++- .../org/specs2/guide/PrintExecutionData.scala | 70 +++--- .../scala/org/specs2/guide/QuickStart.scala | 48 ++-- .../org/specs2/guide/RandomExecution.scala | 20 +- .../guide/ReferenceOtherSpecifications.scala | 44 ++-- .../scala/org/specs2/guide/RunInIDE.scala | 19 +- .../scala/org/specs2/guide/Selection.scala | 133 ++++++----- .../scala/org/specs2/guide/SkipExamples.scala | 17 +- .../guide/SpecificationFormatting.scala | 33 ++- .../specs2/guide/SpecificationTemplate.scala | 15 +- .../scala/org/specs2/guide/Specs2Tags.scala | 22 +- .../org/specs2/guide/StandardResults.scala | 52 ++--- .../scala/org/specs2/guide/Structure.scala | 76 ++++--- .../org/specs2/guide/TimeoutExamples.scala | 17 +- .../org/specs2/guide/TroubleShooting.scala | 14 +- .../guide/UseCommandLineArguments.scala | 63 +++--- .../org/specs2/guide/UseDatatables.scala | 104 +++++---- .../scala/org/specs2/guide/UseForms.scala | 210 ++++++++---------- .../org/specs2/guide/UseScalaCheck.scala | 185 +++++++-------- .../org/specs2/guide/UserGuidePage.scala | 3 +- .../guide/matchers/ContentMatchers.scala | 76 +++---- .../guide/matchers/ExceptionMatchers.scala | 2 +- .../guide/matchers/FutureMatchers.scala | 42 ++-- .../guide/matchers/InterpreterMatchers.scala | 17 +- .../specs2/guide/matchers/JsonMatchers.scala | 46 ++-- .../specs2/guide/matchers/OutsideSpecs2.scala | 24 +- .../guide/matchers/ResultMatchers.scala | 14 +- .../guide/matchers/TerminationMatchers.scala | 32 ++- .../guide/matchers/TraversableMatchers.scala | 60 ++--- .../guide/matchers/TypecheckMatchers.scala | 65 +++--- 54 files changed, 1334 insertions(+), 1417 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 80d0511338..f3cd707ca4 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -8,3 +8,5 @@ align.stripMargin = true align.preset = some spaces.inByNameTypes = false + +project.excludePaths = ["glob:**/guide/**/*.scala"] diff --git a/core/jvm/src/test/scala/org/specs2/specification/SnippetsSpec.scala b/core/jvm/src/test/scala/org/specs2/specification/SnippetsSpec.scala index 864381d7ef..ef6a7b7f24 100644 --- a/core/jvm/src/test/scala/org/specs2/specification/SnippetsSpec.scala +++ b/core/jvm/src/test/scala/org/specs2/specification/SnippetsSpec.scala @@ -58,10 +58,10 @@ Robustness """ def snippet1 = - s2""" code: ${snippet {got {1 + 1}}} """.trimmedTexts(1) === "`got {1 + 1}`" + s2""" code: ${snippet{got {1 + 1}}} """.trimmedTexts(1) === "`got {1 + 1}`" def snippet2 = - s2""" code: ${snippet { + s2""" code: ${snippet{ got { var n = 0 n = 1 @@ -74,7 +74,7 @@ got { |} |```""".stripMargin - def snippet3 = s2""" code: ${snippet { + def snippet3 = s2""" code: ${snippet{ // 8<-- var n = 0 // 8<-- @@ -86,7 +86,7 @@ n = 0 |n = 1 |```""".stripMargin - def snippet4 = s2""" code: ${snippet { + def snippet4 = s2""" code: ${snippet{ // 8<-- var n = 0 // 8<-- @@ -104,9 +104,9 @@ i = 1 |```""".stripMargin def snippet5 = - s2""" code ${snippet { "e1" ! {ok} /**/; 1 /**/ }}""".trimmedTexts(1) === """`"e1" ! {ok} /**/; 1 /**/`""" + s2""" code ${snippet{ "e1" ! {ok} /**/; 1 /**/ }}""".trimmedTexts(1) === """`"e1" ! {ok} /**/; 1 /**/`""" - def offset1 = s2""" code: ${snippet { + def offset1 = s2""" code: ${snippet{ // 8<-- var n = 0 // 8<-- @@ -118,7 +118,7 @@ n = 0 | n = 1 |```""".stripMargin - def offset2 = s2""" code: ${snippet { + def offset2 = s2""" code: ${snippet{ // 8<-- var n = 0 // 8<-- @@ -145,7 +145,7 @@ n = 0 " snippet{ hello \n}.eval " !! "hello" | " snippet{ hello \n}.offsetIs(2) " !! "hello" | { (c, r) => trimApproximatedSnippet(c) === r } - def results1 = s2""" code: ${snippet { + def results1 = s2""" code: ${snippet{ var n = 1 n = 1 + n n @@ -192,7 +192,7 @@ n = 0 def effects2 = var i = 0 - s2""" start ${snippet { i = 1; i }} end """ + s2""" start ${snippet{ i = 1; i }} end """ i === 0 // HELPERS diff --git a/guide/src/test/scala/org/specs2/guide/AddKeywords.scala b/guide/src/test/scala/org/specs2/guide/AddKeywords.scala index 46bc8add04..c6da400722 100644 --- a/guide/src/test/scala/org/specs2/guide/AddKeywords.scala +++ b/guide/src/test/scala/org/specs2/guide/AddKeywords.scala @@ -3,21 +3,20 @@ package org.specs2.guide object AddKeywords extends UserGuidePage { def is = s2""" -Mutable specifications offer a predefined "vocabulary" to define examples: ${snippet { - import org.specs2.* +Mutable specifications offer a predefined "vocabulary" to define examples: ${snippet{ +import org.specs2.* - class MySpecification extends mutable.Specification: +class MySpecification extends mutable.Specification: + "the 'and' function" should { + "return true when passed true, true" >> { + (true && true) === true + } + "return false when passed true, false" >> { + (true && false) === false + } + } - "the 'and' function" should { - "return true when passed true, true" >> { - (true && true) === true - } - "return false when passed true, false" >> { - (true && false) === false - } - } - - }} +}} This will print: ``` @@ -27,33 +26,33 @@ This will print: ``` And you can see that the word "should" has been added to the first description. -However one size does not fit all and you might want to add your own predefined words. Here is how to do it: ${snippet { - import org.specs2.* - import org.specs2.specification.core.{Fragment, Fragments} - import org.specs2.specification.dsl.mutable.* - import org.specs2.control.ImplicitParameters +However one size does not fit all and you might want to add your own predefined words. Here is how to do it: ${snippet{ +import org.specs2.* +import org.specs2.specification.core.{Fragment, Fragments} +import org.specs2.specification.dsl.mutable.* +import org.specs2.control.ImplicitParameters - trait ToKeyword extends ExtendedBlockDsl: - extension (description: String) - infix def to(f: =>Fragment): Fragment = - (description + " to") >> f +trait ToKeyword extends ExtendedBlockDsl: + extension (description: String) + infix def to(f: =>Fragment): Fragment = + (description + " to") >> f - // this implementation of `to` uses an implicit parameter. This is used to overload - // the method for different arguments: Fragment and Fragments - infix def to(fs: =>Fragments)(using p1: ImplicitParameters.ImplicitParam1): Fragments = - (description + " to") >> fs + // this implementation of `to` uses an implicit parameter. This is used to overload + // the method for different arguments: Fragment and Fragments + infix def to(fs: =>Fragments)(using p1: ImplicitParameters.ImplicitParam1): Fragments = + (description + " to") >> fs - class MySpecification extends org.specs2.mutable.Specification with ToKeyword: +class MySpecification extends org.specs2.mutable.Specification with ToKeyword: - "the 'and' function is used" to { - "return true when passed true, true" >> { - (true && true) === true - } - "return false when passed true, false" >> { - (true && false) === false - } - } - }} + "the 'and' function is used" to { + "return true when passed true, true" >> { + (true && true) === true + } + "return false when passed true, false" >> { + (true && false) === false + } + } +}} Now this will print ``` @@ -61,7 +60,6 @@ Now this will print + return true when passed true, true + return false when passed true, false ``` - """ } diff --git a/guide/src/test/scala/org/specs2/guide/ArgumentsReference.scala b/guide/src/test/scala/org/specs2/guide/ArgumentsReference.scala index caed3f0f51..eb182c4617 100644 --- a/guide/src/test/scala/org/specs2/guide/ArgumentsReference.scala +++ b/guide/src/test/scala/org/specs2/guide/ArgumentsReference.scala @@ -6,16 +6,16 @@ import org.specs2.main.FilesRunnerArguments.* object ArgumentsReference extends UserGuidePage { def is = "Arguments reference".title ^ s2""" -Arguments can be passed on the command line, or declared inside a specification, using the `args(name=value)` syntax:${snippet { +Arguments can be passed on the command line, or declared inside a specification, using the `args(name=value)` syntax:${snippet{ - class MySpec extends Specification: - def is = args(xonly = true) ^ s2""" +class MySpec extends Specification: + def is = args(xonly = true) ^ s2""" Clever spec title And some intro text brilliant expectation $success """ - }} +}} They can also be passed as system properties: `-Dspecs2.name=value` (`-Dname=value` also works but you might have collisions with other properties). @@ -131,7 +131,7 @@ For ${"the HTML output" ~/ HtmlOutput} the following options can be used: ## Arguments API -From inside a specification, the `args` method provides the most frequent arguments as `args(argumentName = argumentValue)`. In the least frequent cases you will have to write:${snippet { +From inside a specification, the `args` method provides the most frequent arguments as `args(argumentName = argumentValue)`. In the least frequent cases you will have to write:${snippet{ // for selection arguments args.select(ex = "example \\d*") diff --git a/guide/src/test/scala/org/specs2/guide/AsResultTypeclass.scala b/guide/src/test/scala/org/specs2/guide/AsResultTypeclass.scala index 0c0f9c6333..6be7c914d4 100644 --- a/guide/src/test/scala/org/specs2/guide/AsResultTypeclass.scala +++ b/guide/src/test/scala/org/specs2/guide/AsResultTypeclass.scala @@ -15,29 +15,28 @@ There are many ways to define expectations in $specs2: * Forms $p -All of these types implement the `org.specs2.execute.AsResult` typeclass, meaning that they can be transformed into a `Result`:${snippet { - trait AsResult[T] { - def asResult(t: =>T): Result - } - }} +All of these types implement the `org.specs2.execute.AsResult` typeclass, meaning that they can be transformed into a `Result`:${snippet{ +trait AsResult[T]: + def asResult(t: =>T): Result +}} This gives some flexibility in integrating any kind of custom definition of a "result" into $specs2 and this is why you find this typeclass as a requirement to build examples or to declare contexts. -You can take advantage of this type class by defining your own kind of result and providing a typeclass instance for it:${snippet { +You can take advantage of this type class by defining your own kind of result and providing a typeclass instance for it:${snippet{ // A new type of results for cluster execution - trait ClusterExecution: - def succeeded: Boolean - def errorMessage: String - - object ClusterExecution: - given AsResult[ClusterExecution] = - new AsResult[ClusterExecution]: - def asResult(t: =>ClusterExecution): Result = - try { - val result = t - if (result.succeeded) Success() - else Failure(t.errorMessage) - } catch { case e: Throwable => Error(e) } - }} +trait ClusterExecution: + def succeeded: Boolean + def errorMessage: String + +object ClusterExecution: + given AsResult[ClusterExecution] = + new AsResult[ClusterExecution]: + def asResult(t: =>ClusterExecution): Result = + try { + val result = t + if (result.succeeded) Success() + else Failure(t.errorMessage) + } catch { case e: Throwable => Error(e) } +}} #### Decorated results diff --git a/guide/src/test/scala/org/specs2/guide/AutoExamples.scala b/guide/src/test/scala/org/specs2/guide/AutoExamples.scala index 4d01e25aa4..bf8afbc58b 100644 --- a/guide/src/test/scala/org/specs2/guide/AutoExamples.scala +++ b/guide/src/test/scala/org/specs2/guide/AutoExamples.scala @@ -6,21 +6,22 @@ import collection.Seqx.* object AutoExamples extends UserGuidePage { def is = "Auto-examples".title ^ s2""" -When you want to specify an API, most of your examples are self-describing:${snippet { - class SeqSpecification extends mutable.Specification: - "updateLast modifies the last element of a Seq".p - "when the collection has 1 element" >> { Seq(1).updateLast(_ + 1) must ===(Seq(2)) } - "when the collection has 2 elements" >> { Seq(1, 2).updateLast(_ + 1) must ===(Seq(1, 3)) } - "when the collection is empty" >> { Seq[Int]().updateLast(_ + 1) must ===(Seq[Int]()) } - }} - -It is a bit redundant to provide a textual description for these 3 examples because the code is pretty clear and simple. In this situation you can use the `eg` operator to create an example where the description will be the code itself:${snippet { - class SeqSpecification extends mutable.Specification: - "updateLast modifies the last element of a Seq".p - eg { Seq(1).updateLast(_ + 1) must ===(Seq(2)) } - eg { Seq(1, 2).updateLast(_ + 1) must ===(Seq(1, 3)) } - eg { Seq[Int]().updateLast(_ + 1) must ===(Seq[Int]()) } - }} +When you want to specify an API, most of your examples are self-describing:${snippet{ +class SeqSpecification extends mutable.Specification: + "updateLast modifies the last element of a Seq".p + "when the collection has 1 element" >> { Seq(1).updateLast(_ + 1) must ===(Seq(2)) } + "when the collection has 2 elements" >> { Seq(1, 2).updateLast(_ + 1) must ===(Seq(1, 3)) } + "when the collection is empty" >> { Seq[Int]().updateLast(_ + 1) must ===(Seq[Int]()) } +}} + +It is a bit redundant to provide a textual description for these 3 examples because the code is pretty clear and simple. +In this situation you can use the `eg` operator to create an example where the description will be the code itself:${snippet{ +class SeqSpecification extends mutable.Specification: + "updateLast modifies the last element of a Seq".p + eg { Seq(1).updateLast(_ + 1) must ===(Seq(2)) } + eg { Seq(1, 2).updateLast(_ + 1) must ===(Seq(1, 3)) } + eg { Seq[Int]().updateLast(_ + 1) must ===(Seq[Int]()) } +}} This prints: ``` @@ -32,27 +33,25 @@ This prints: #### In an acceptance specification -Acceptance specifications are using interpolated strings so you can directly write:${snippet { - class SeqSpecification extends Specification: - def is = s2""" +Acceptance specifications are using interpolated strings so you can directly write:${snippet{ +class SeqSpecification extends Specification: + def is = s2""" updateLast modifies the last element of a Seq ${Seq(1).updateLast(_ + 1) must ===(Seq(2))} ${Seq(1, 2).updateLast(_ + 1) must ===(Seq(1, 3))} ${Seq[Int]().updateLast(_ + 1) must ===(Seq[Int]())} """ +}} - }} - -There is a huge gotcha though! Each of these expressions needs an implicit conversion to be included in the interpolated spec. And in Scala, if you have a block of code returning a value of type `T`, ***only the last expression of the block is converted***. This means that if there is a statement in the block that throws an exception, this exception won't be caught and the whole specification will fail to be instantiated! So if you want to use blocks as auto-examples you should better wrap them with an `eg` call:${snippet { - class SeqSpecification extends Specification: - def is = s2""" +There is a huge gotcha though! Each of these expressions needs an implicit conversion to be included in the interpolated spec. And in Scala, if you have a block of code returning a value of type `T`, ***only the last expression of the block is converted***. This means that if there is a statement in the block that throws an exception, this exception won't be caught and the whole specification will fail to be instantiated! So if you want to use blocks as auto-examples you should better wrap them with an `eg` call:${snippet{ +class SeqSpecification extends Specification: + def is = s2""" This is a problematic specification ${ sys.error("ouch, this one is going to blow up the spec"); Seq(1).updateLast(_ + 1) must ===(Seq(2)) } ${eg { sys.error("it's ok, this one is well protected"); Seq(1).updateLast(_ + 1) must ===(Seq(2)) }} """ - }} - +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/CaptureSnippets.scala b/guide/src/test/scala/org/specs2/guide/CaptureSnippets.scala index ed9be1a402..35c75dce87 100644 --- a/guide/src/test/scala/org/specs2/guide/CaptureSnippets.scala +++ b/guide/src/test/scala/org/specs2/guide/CaptureSnippets.scala @@ -16,7 +16,7 @@ Here is an example of using the `snippet` method: ``` s2$triple -This is a multi-line string with a code snippet: $${ snippet { +This is a multi-line string with a code snippet: $${snippet{ def factorial(n: Int): Int = if (n == 1) n else (n * factorial(n - 1)) factorial(3) == 6 }} @@ -38,7 +38,7 @@ Since snippets are compiled code, you might have to include many declarations, l ``` s2$triple -This is a snippet of code with one relevant line: $${ snippet { +This is a snippet of code with one relevant line: $${snippet{ // 8<-- def factorial(n: Int): Int = if (n == 1) n else (n * factorial(n - 1)) // 8<-- @@ -52,7 +52,7 @@ The snippet above will only show `factorial(3) == 6`. You can repeat this patter ``` s2$triple -This is a snippet of code with 2 relevant lines: $${ snippet { +This is a snippet of code with 2 relevant lines: $${snippet{ // 8<-- def factorial(n: Int): Int = if (n == 1) n else (n * factorial(n - 1)) // 8<-- @@ -78,7 +78,7 @@ By default the last value of a Snippet is not shown but you can display it with ``` s2$triple -This is a snippet of code with a result: $${ snippet { +This is a snippet of code with a result: $${snippet{ factorial(3) }.eval} $triple @@ -99,7 +99,7 @@ It is possible to adjust the margin of captured source code by adding or removin ``` s2$triple -This is a snippet of code with a negative offset to align the code to the border of the screen: $${ snippet { +This is a snippet of code with a negative offset to align the code to the border of the screen: $${snippet{ def factorial(n: Int): Int = if (n == 1) n else (n * factorial(n - 1)) factorial(3) }.offsetIs(-3)} diff --git a/guide/src/test/scala/org/specs2/guide/Contexts.scala b/guide/src/test/scala/org/specs2/guide/Contexts.scala index defb98ad68..ec1537fc34 100644 --- a/guide/src/test/scala/org/specs2/guide/Contexts.scala +++ b/guide/src/test/scala/org/specs2/guide/Contexts.scala @@ -26,17 +26,17 @@ For all those situations, there is a $specs2 trait which you can mix in your spe ### BeforeEach / AfterEach -The `org.specs2.specification.BeforeEach` trait defines an action that will be executed before each example:${snippet { - class BeforeSpecification extends org.specs2.mutable.Specification with BeforeEach: - // you need to define the "before" action - def before = step(println("before")) - "example 1" >> { - println("example1"); ok - } - "example 2" >> { - println("example2"); ok - } - }} +The `org.specs2.specification.BeforeEach` trait defines an action that will be executed before each example:${snippet{ +class BeforeSpecification extends org.specs2.mutable.Specification with BeforeEach: + // you need to define the "before" action + def before = step(println("before")) + "example 1" >> { + println("example1"); ok + } + "example 2" >> { + println("example2"); ok + } +}} If you execute this specification you may see something like (note that examples and before actions are executed concurrently): ```console @@ -46,32 +46,32 @@ If you execute this specification you may see something like (note that examples [info] example1 ``` -As you can guess, defining a behaviour "after" is very similar:${snippet { - class AfterSpecification extends org.specs2.mutable.Specification with AfterEach: - // you need to define the "after" action - def after = step(println("after")) - - "example 1" >> { - println("example1"); ok - } - "example 2" >> { - println("example2"); ok - } - }} +As you can guess, defining a behaviour "after" is very similar:${snippet{ +class AfterSpecification extends org.specs2.mutable.Specification with AfterEach: + // you need to define the "after" action + def after = step(println("after")) + + "example 1" >> { + println("example1"); ok + } + "example 2" >> { + println("example2"); ok + } +}} -You might also want to mix the two:${snippet { - class BeforeAfterSpecification extends org.specs2.mutable.Specification with BeforeAfterEach: +You might also want to mix the two:${snippet{ +class BeforeAfterSpecification extends org.specs2.mutable.Specification with BeforeAfterEach: - def before = step(println("before")) - def after = step(println("after")) + def before = step(println("before")) + def after = step(println("after")) - "example 1" >> { - println("example1"); ok - } - "example 2" >> { - println("example2"); ok - } - }} + "example 1" >> { + println("example1"); ok + } + "example 2" >> { + println("example2"); ok + } +}} _IMPORTANT_: Mixing traits like `BeforeEach` and `BeforeAfterEach` can lead to surprising behaviour where the `before` action is executed twice. You should rather have both traits extends `BeforeAfterEach` to avoid that: @@ -90,26 +90,26 @@ trait B2 extends BeforeAfterEach with ActionDsl: ### AroundEach Another very common situation is when you need to execute in the context of a database transaction or a web request. -In this case you can use the `AroundEach` trait to execute each example in the proper context:${snippet { - trait DatabaseContext extends AroundEach: - // you need to define the "around" method - def around[R: AsResult](r: =>R): Result = - openDatabaseTransaction - try AsResult(r) - finally closeDatabaseTransaction - - // do what you need to do with the database - def openDatabaseTransaction = ??? - def closeDatabaseTransaction = ??? - - class AroundSpecification extends org.specs2.mutable.Specification with DatabaseContext: - "example 1" >> { - println("using the database"); ok - } - "example 2" >> { - println("using the database too"); ok - } - }} +In this case you can use the `AroundEach` trait to execute each example in the proper context:${snippet{ +trait DatabaseContext extends AroundEach: + // you need to define the "around" method + def around[R: AsResult](r: =>R): Result = + openDatabaseTransaction + try AsResult(r) + finally closeDatabaseTransaction + + // do what you need to do with the database + def openDatabaseTransaction = ??? + def closeDatabaseTransaction = ??? + +class AroundSpecification extends org.specs2.mutable.Specification with DatabaseContext: + "example 1" >> { + println("using the database"); ok + } + "example 2" >> { + println("using the database too"); ok + } +}} The specification above shows a trait `DatabaseContext` extending `AroundEach` (so that trait can be reused for other specifications). It defines a method named `around` taking the body of the example, anything with an ${"AsResult" ~/ AsResultTypeclass} typeclass, and returns a result. Because `r` is a byname parameter, you are free to do whatever you want before or after evaluating it, like opening and closing a database transaction. @@ -125,32 +125,32 @@ There is however one thing you cannot do with `AroundExample`. You can't pass a ### ForEach Sometimes you need to manage a specific context for each example but you also want to make it accessible to the examples themselves. -Here is a specification having examples using an active database transaction:${snippet { +Here is a specification having examples using an active database transaction:${snippet{ // a transaction with the database - trait Transaction - - trait DatabaseContext extends ForEach[Transaction]: - // you need to define the "foreach" method - def foreach[R: AsExecution](f: Transaction => R): R = - val transaction = openDatabaseTransaction - try f(transaction) - finally closeDatabaseTransaction(transaction) - - // create and close a transaction - def openDatabaseTransaction: Transaction = ??? - - def closeDatabaseTransaction(t: Transaction) = ??? - - class FixtureSpecification extends org.specs2.mutable.Specification with DatabaseContext: - "example 1" >> { (t: Transaction) => - println("use the transaction") - ok - } - "example 2" >> { (t: Transaction) => - println("use it here as well") - ok - } - }} +trait Transaction + +trait DatabaseContext extends ForEach[Transaction]: + // you need to define the "foreach" method + def foreach[R: AsExecution](f: Transaction => R): R = + val transaction = openDatabaseTransaction + try f(transaction) + finally closeDatabaseTransaction(transaction) + + // create and close a transaction + def openDatabaseTransaction: Transaction = ??? + + def closeDatabaseTransaction(t: Transaction) = ??? + +class FixtureSpecification extends org.specs2.mutable.Specification with DatabaseContext: + "example 1" >> { (t: Transaction) => + println("use the transaction") + ok + } + "example 2" >> { (t: Transaction) => + println("use it here as well") + ok + } +}} ### BeforeSpec / AfterSpec @@ -208,7 +208,6 @@ class ResourceExample(using ec: ExecutionContext) extends Specification, LocalRe ref.update(v => v + 1) ok } - ``` In this example we use a mutable reference as our "expensive" resource. It is created with the `acquire` method which returns @@ -218,7 +217,7 @@ some files are closed, ...) with the `release` method. You will need to return a be converted to a specs2 `Execution`: a `Result`, a `Future[Result]` or even a simple `Boolean`. Sometimes it is necessary to keep a resource open across several specifications invocations. In order to do this you need to -override the `resourceKey` function to provide a unique key for the resource: ${snippet { +override the `resourceKey` function to provide a unique key for the resource: ${snippet{ // 8<--- import org.specs2.Specification import org.specs2.specification.Resource diff --git a/guide/src/test/scala/org/specs2/guide/CreateOnlineSpecifications.scala b/guide/src/test/scala/org/specs2/guide/CreateOnlineSpecifications.scala index e27cc3a721..3c99ebbac6 100644 --- a/guide/src/test/scala/org/specs2/guide/CreateOnlineSpecifications.scala +++ b/guide/src/test/scala/org/specs2/guide/CreateOnlineSpecifications.scala @@ -14,52 +14,48 @@ object CreateOnlineSpecifications extends UserGuidePage { $p More precisely we want to create one example for `1.` and if it succeeds, create as many examples as there are links in `2.`. -This can be done with the `org.specs2.specification.dsl.Online` trait and the `continueWith` method: ${snippet { +This can be done with the `org.specs2.specification.dsl.Online` trait and the `continueWith` method: ${snippet{ // fill in the definitions below - object Wikipedia: - def getPages(searchTerm: String): Seq[Page] = ??? +object Wikipedia: + def getPages(searchTerm: String): Seq[Page] = ??? - trait Page: - def getLinks: Seq[HtmlLink] = ??? +trait Page: + def getLinks: Seq[HtmlLink] = ??? - trait HtmlLink: - def contains(name: String): Boolean = ??? - def getName: String = ??? - def getLinkedPage: Page = ??? +trait HtmlLink: + def contains(name: String): Boolean = ??? + def getName: String = ??? + def getLinkedPage: Page = ??? // 8<---- - class WikipediaBddSpec extends Specification with Online { - def is = s2""" +class WikipediaBddSpec extends Specification with Online: + def is = s2""" All the pages mentioning the term BDD must contain a reference to specs2 $e1 """ - def e1 = { - val pages = Wikipedia.getPages("BDD") - - { pages must contain((_: Page) must mention("specs2")) } continueWith - pagesSpec(pages) - } + def e1 = + val pages = Wikipedia.getPages("BDD") + { pages must contain((_: Page) must mention("specs2")) } continueWith + pagesSpec(pages) - def pagesSpec(pages: Seq[Page]): Fragments = { - val specs2Links = pages.flatMap(_.getLinks).filter(_.contains("specs2")) + def pagesSpec(pages: Seq[Page]): Fragments = + val specs2Links = pages.flatMap(_.getLinks).filter(_.contains("specs2")) - s2""" - The specs2 links must all be active - ${Fragments.foreach(specs2Links)(isActive)} - """ - } + s2""" + The specs2 links must all be active + ${Fragments.foreach(specs2Links)(isActive)} + """ - def isActive(link: HtmlLink) = - s2""" + def isActive(link: HtmlLink) = + s2""" The page at ${link.getName} must be active ${link must beActive}""" - // implement this matcher - def mention(name: String): Matcher[Page] = ??? - def beActive: Matcher[HtmlLink] = ??? - } - }} + // implement these matchers + def mention(name: String): Matcher[Page] = ??? + def beActive: Matcher[HtmlLink] = ??? +}} In the specification above, if we succeed in checking each BDD page then we continue with the creation of individual examples for each encountered link. diff --git a/guide/src/test/scala/org/specs2/guide/Environment.scala b/guide/src/test/scala/org/specs2/guide/Environment.scala index 7f97f7fc36..d11108f6ea 100644 --- a/guide/src/test/scala/org/specs2/guide/Environment.scala +++ b/guide/src/test/scala/org/specs2/guide/Environment.scala @@ -32,36 +32,33 @@ The following objects can be injected in your specification if you declare a 1-p - the `ExecutionEnv` object (can be implicit) - the `ExecutionContext` object (can be implicit) -

For example: ${snippet { - class MySpec(env: Env) extends Specification { - def is = s2""" +

For example: ${snippet{ +class MySpec(env: Env) extends Specification: + def is = s2""" Use the environment fileSystem ${ env.fileSystem.mkdirs("tmp" / "test").runOption; ok } -""" - } - }} - -Or if you want to access an `ExecutionContext`:${snippet { - class MySpec(using ec: ExecutionContext) extends Specification { - def is = s2""" - Use a future - ${Await.result(Future(1), 1.seconds) must ===(1)}) -""" - } - }} + """ +}} + +Or if you want to access an `ExecutionContext`:${snippet{ +class MySpec(using ec: ExecutionContext) extends Specification: + def is = s2""" + Use a future + ${Await.result(Future(1), 1.seconds) must ===(1)}) + """ +}} ### Own Env / ExecutionEnvironment The `ExecutionEnv` which is injected in a specification will be shared with all specifications. If you want to provide some isolation between your specifications and get a specific thread pool being dedicated to your specification you use -the `org.specs2.specification.core.OwnEnv` or `org.specs2.specification.core.OwnExecutionEnv` traits:${snippet { - class MySpec(val env: Env) extends Specification with OwnExecutionEnv { - def is = s2""" - Use a future - ${Await.result(Future(1), 1.seconds) must ===(1)}) -""" - } - }} +the `org.specs2.specification.core.OwnEnv` or `org.specs2.specification.core.OwnExecutionEnv` traits:${snippet{ +class MySpec(val env: Env) extends Specification with OwnExecutionEnv: + def is = s2""" + Use a future + ${Await.result(Future(1), 1.seconds) must ===(1)}) + """ +}} You need to inject a public `env` which will be duplicated to create an implicit `ExecutionEnv` for the sole use of your specification (and shutdown when your specification has been executed). Doing so ensures that command line arguments diff --git a/guide/src/test/scala/org/specs2/guide/ExampleDescription.scala b/guide/src/test/scala/org/specs2/guide/ExampleDescription.scala index 7148078e95..eebd65a668 100644 --- a/guide/src/test/scala/org/specs2/guide/ExampleDescription.scala +++ b/guide/src/test/scala/org/specs2/guide/ExampleDescription.scala @@ -7,62 +7,61 @@ object ExampleDescription extends UserGuidePage { def is = s2""" ["Don't repeat yourself"](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) mandates that the same information is not repeated twice. -However this situation happens when part of an example description is reused in the example body: ${snippet { - s2""" +However this situation happens when part of an example description is reused in the example body: ${snippet{ +s2""" 1971-01-01 is a correct date $correct """ - - def correct = { "1971-01-01" must beCorrect } - }} +def correct = { "1971-01-01" must beCorrect } +}} ### Use the example description -You can avoid this by creating the example body as a function using the description string: ${snippet { - s2""" - 1971-01-01 is a correct date $correct +You can avoid this by creating the example body as a function using the description string: ${snippet{ +s2""" + 1971-01-01 is a correct date $correct """ - def correct = { (date: String) => - date.split(" ")(0) must beCorrect - } - }} +def correct = { (date: String) => + date.split(" ")(0) must beCorrect +} +}} ### Parse the example description #### Delimited values -We can reuse the `StepParsers` to extract the values we wish to use: ${snippet { - s2""" - {1971-01-01} is a correct date $correct - {1} plus {1} is {2} $addition +We can reuse the `StepParsers` to extract the values we wish to use: ${snippet{ +s2""" + {1971-01-01} is a correct date $correct + {1} plus {1} is {2} $addition """ - import org.specs2.specification.script.StepParsers.* - import org.specs2.specification.script.StepParsers.given +import org.specs2.specification.script.StepParsers.* +import org.specs2.specification.script.StepParsers.given - def correct = extract { (date: String) => - date must beCorrect - } +def correct = extract { (date: String) => + date must beCorrect +} - def addition = extract { (a: String, b: String, c: String) => - a.toInt + b.toInt must ===(c.toInt) - } - }} +def addition = extract { (a: String, b: String, c: String) => + a.toInt + b.toInt must ===(c.toInt) +} +}} The values to be extracted are delimited by `{}` and those curly braces will not be displayed when the specification is reported. #### Standard delimited parsers -When you parse values with `extract` you get only Strings which you have to transform into `Int` for example. $specs2 comes up with a few predefined parsers to help you with that: ${snippet { - s2""" - {1} plus {1} is {2} $addition +When you parse values with `extract` you get only Strings which you have to transform into `Int` for example. $specs2 comes up with a few predefined parsers to help you with that: ${snippet{ +s2""" + {1} plus {1} is {2} $addition """ - import org.specs2.specification.script.StandardDelimitedStepParsers.* +import org.specs2.specification.script.StandardDelimitedStepParsers.* - def addition = threeInts.map { case (a, b, c) => - a + b must ===(c) - } - }} +def addition = threeInts.map { case (a, b, c) => + a + b must ===(c) +} +}} The other parsers are: @@ -81,38 +80,38 @@ The other parsers are: #### Regular expressions -Another way to extract values is to use regular expressions to extract similar groups of values. In that case no delimiters are required. For example: ${snippet { - s2""" - 1971-01-01 is a correct date $correct - 1 plus 1 is 2 $addition +Another way to extract values is to use regular expressions to extract similar groups of values. In that case no delimiters are required. For example: ${snippet{ +s2""" + 1971-01-01 is a correct date $correct + 1 plus 1 is 2 $addition """ - import org.specs2.specification.script.StepParsers.* +import org.specs2.specification.script.StepParsers.* - // groupAs is equivalent to running 'regexp findAllIn text' - // and getting one argument per match group found - def correct = groupAs("[^ ]+").and { (date: String) => - date must beCorrect - } +// groupAs is equivalent to running 'regexp findAllIn text' +// and getting one argument per match group found +def correct = groupAs("[^ ]+").and { (date: String) => + date must beCorrect +} - def addition = groupAs("\\d+").and { (a: String, b: String, c: String) => - a.toInt + b.toInt must ===(c.toInt) - } - }} +def addition = groupAs("\\d+").and { (a: String, b: String, c: String) => + a.toInt + b.toInt must ===(c.toInt) +} +}} #### Standard regexp parsers -Similarly to delimited parsers, there are some predefined regexp parsers: ${snippet { - s2""" - 1 plus 1 is 2 $addition +Similarly to delimited parsers, there are some predefined regexp parsers: ${snippet{ +s2""" + 1 plus 1 is 2 $addition """ - import org.specs2.specification.script.StandardRegexStepParsers.* +import org.specs2.specification.script.StandardRegexStepParsers.* - def addition = threeInts.map { case (a, b, c) => - a + b must ===(c) - } - }} +def addition = threeInts.map { case (a, b, c) => + a + b must ===(c) +} +}} """ def beCorrect: Matcher[String] = ??? diff --git a/guide/src/test/scala/org/specs2/guide/Execution.scala b/guide/src/test/scala/org/specs2/guide/Execution.scala index 0175d75843..f5e7614310 100644 --- a/guide/src/test/scala/org/specs2/guide/Execution.scala +++ b/guide/src/test/scala/org/specs2/guide/Execution.scala @@ -16,9 +16,9 @@ Starting from this default you can progressively add constraints to get more con ### Steps -A `Step` is an action which can be executed anywhere in a specification. When you declare a `Step` like this:${snippet { - class StepSpec extends Specification: - def is = s2""" +A `Step` is an action which can be executed anywhere in a specification. When you declare a `Step` like this:${snippet{ +class StepSpec extends Specification: + def is = s2""" this is example 1 $ok this is example 2 $ok @@ -27,7 +27,7 @@ A `Step` is an action which can be executed anywhere in a specification. When yo this is example 3 $ok this is example 4 $ok """ - }} +}} Then the specification will: @@ -40,9 +40,9 @@ There is no "result" for a step but if it throws an Exception an `Error` will be ### Stop the execution -You can still control if the rest of the specification must be executed by adding some constraints on the step. For example:${snippet { - class StepWithStopOnErrorSpec extends Specification: - def is = s2""" +You can still control if the rest of the specification must be executed by adding some constraints on the step. For example:${snippet{ +class StepWithStopOnErrorSpec extends Specification: + def is = s2""" this is example 1 $ok this is example 2 $ok @@ -51,7 +51,7 @@ You can still control if the rest of the specification must be executed by addin this is example 3 $ok this is example 4 $ok """ - }} +}} When this specification is executed examples 3 and 4 will be skipped because the step returns an `Error`. An `Error` is likely to be a fatal condition but you can use other methods to stop the execution: @@ -61,24 +61,24 @@ When this specification is executed examples 3 and 4 will be skipped because the ### Sequential -If your specification is a list of well-ordered examples you can use the `sequential` argument to make sure that they are executed in order:${snippet { - class SequentialSpec extends Specification: - def is = sequential ^ s2""" +If your specification is a list of well-ordered examples you can use the `sequential` argument to make sure that they are executed in order:${snippet{ +class SequentialSpec extends Specification: + def is = sequential ^ s2""" this is example 1 $ok this is example 2 $ok this is example 3 $ok this is example 4 $ok """ - }} +}} Thanks to the `sequential` argument the 4 examples above will execute one after the other. ### Action -Finally if you want to execute "silent" actions, like steps, but with no impact on the sequencing of the specification, you can use an `Action`:${snippet { - class ActionSpec extends Specification: - def is = s2""" +Finally if you want to execute "silent" actions, like steps, but with no impact on the sequencing of the specification, you can use an `Action`:${snippet{ +class ActionSpec extends Specification: + def is = s2""" this is example 1 $ok this is example 2 $ok @@ -89,7 +89,7 @@ Finally if you want to execute "silent" actions, like steps, but with no impact this is example 3 $ok this is example 4 $ok """ - }} +}} $NowLearnTo diff --git a/guide/src/test/scala/org/specs2/guide/ExecutionEnvironments.scala b/guide/src/test/scala/org/specs2/guide/ExecutionEnvironments.scala index 45b138c19a..bcc810a3d3 100644 --- a/guide/src/test/scala/org/specs2/guide/ExecutionEnvironments.scala +++ b/guide/src/test/scala/org/specs2/guide/ExecutionEnvironments.scala @@ -16,32 +16,30 @@ You can access this `ExecutorService` to execute futures (from Scala, Scalaz etc ### Scala Future A Scala `Future` needs an implicit `ExecutionContext` to be created. You can get an execution context, out of the box, shared across all -specifications by declaring it as a class member: ${snippet { - class MyFutureSpec(using ec: ExecutionContext) extends Specification { - def is = s2""" - Let's check this scala future ${Await.result(Future(1), Duration.Inf) must ===(1)} -""" - } +specifications by declaring it as a class member: ${snippet{ +class MyFutureSpec(using ec: ExecutionContext) extends Specification: + def is = s2""" + Let's check this scala future ${Await.result(Future(1), Duration.Inf) must ===(1)} + """ // in a mutable specification - class MyMutableFutureSpec(using ec: ExecutionContext) extends mutable.Specification { - "Let's check this scala future" >> { - Await.result(Future(1), Duration.Inf) must ===(1) - } - } - }} - -You can also use an `ExecutionEnv` (from now on code examples are provided for immutable specifications only but are transposable to mutable ones): ${snippet { - class MyFutureSpec(using ee: ExecutionEnv) extends Specification { - def is = s2""" - Let's check this scala future ${Await.result(Future(1), Duration.Inf) must ===(1)} -""" - } - }} +class MyMutableFutureSpec(using ec: ExecutionContext) extends mutable.Specification: + "Let's check this scala future" >> { + Await.result(Future(1), Duration.Inf) must ===(1) + } +}} + +You can also use an `ExecutionEnv` (from now on code examples are provided for immutable specifications only but are transposable to mutable ones): ${snippet{ +class MyFutureSpec(using ee: ExecutionEnv) extends Specification: + def is = s2""" + Let's check this scala future ${Await.result(Future(1), Duration.Inf) must ===(1)} + """ +}} This works thanks to an implicit conversion between `ExecutionEnv` and `ExecutionContext` provided by the `org.specs2.execute.ImplicitExecutionContextFromExecutionEnv` trait (this can be deactivated by mixing-in the `NoImplicitExecutionContextFromExecutionEnv` trait). + It is actually better to use an `ExecutionEnv` anyway because it is required when you want to ${"create `Future` matchers" ~/ Matchers} (see the "Future" tab). Indeed an `ExecutionEnv` contains a `timeFactor` which can be used to modify the timeout from the command line and wait longer for Futures executing on a continuous integration server for example. @@ -50,14 +48,15 @@ wait longer for Futures executing on a continuous integration server for example Future $Matchers (see the "Future" tab) require an implicit `ExecutionEnv`. This environment is used to access the `timeFactor` when awaiting for Scala Futures. -The `terminate` matcher (see the "Termination" tab in the optional $Matchers section) also needs an `ExecutionEnv` to run a piece of code and periodically check if it has terminated or not: ${snippet { - s2""" +The `terminate` matcher (see the "Termination" tab in the optional $Matchers section) also needs an `ExecutionEnv` to run a piece of code and periodically +check if it has terminated or not: ${snippet{ +s2""" this code must be fast enough ${ given ExecutionEnv = ExecutionEnv.fromGlobalExecutionContext Thread.sleep(100) must terminate(retries = 1, sleep = 60.millis) - } + } """ - }} +}} ### One per specification diff --git a/guide/src/test/scala/org/specs2/guide/ExpectationDescription.scala b/guide/src/test/scala/org/specs2/guide/ExpectationDescription.scala index b525a7f9e2..b8190a42cf 100644 --- a/guide/src/test/scala/org/specs2/guide/ExpectationDescription.scala +++ b/guide/src/test/scala/org/specs2/guide/ExpectationDescription.scala @@ -7,19 +7,19 @@ object ExpectationDescription extends UserGuidePage { def is = s2""" #### Enhance failures messages -Some expressions using matchers might not produce very useful messages. For example: ${snippet { - val ticketsNumber = 5 +Some expressions using matchers might not produce very useful messages. For example: ${snippet{ +val ticketsNumber = 5 // will fail with '5' is not equal to '3' - ticketsNumber must be_==(3) - }} +ticketsNumber must be_==(3) +}} -You can improve this failure message by describing what `ticketsNumber` represents: ${snippet { - val ticketsNumber = 5 +You can improve this failure message by describing what `ticketsNumber` represents: ${snippet{ +val ticketsNumber = 5 // will fail with "the number of tickets '5' is not equal to '3'" - ticketsNumber aka "the number of tickets" must be_==(3) - }} +ticketsNumber aka "the number of tickets" must be_==(3) +}} The `aka` (*also known as*) method has a few variations: @@ -34,17 +34,17 @@ On a `Matcher` or a `Result` you can use `updateMessage(f: String => String)` or #### Describe an expectation -Another way to provide a description for an expectation is to use the `==>` (or `<==>`) operator:${snippet { - s2"""A byname function can be transformed into a strict one $e1""" +Another way to provide a description for an expectation is to use the `==>` (or `<==>`) operator:${snippet{ +s2"""A byname function can be transformed into a strict one $e1""" - def e1 = { - def byNameFunction(u: =>Unit): Unit = {} - var parameter = "not evaluated" - toStrictFunction1(byNameFunction) { parameter = "evaluated" } +def e1 = { + def byNameFunction(u: =>Unit): Unit = {} + var parameter = "not evaluated" + toStrictFunction1(byNameFunction) { parameter = "evaluated" } - "The byname function has become a strict one" ==> (parameter === "evaluated") - } - }} + "The byname function has become a strict one" ==> (parameter === "evaluated") +} +}} In the example above, testing if `parameter == "evaluated"` is just a way to observe what we wanted to achieve. If that doesn't work, the failure message will be ``` diff --git a/guide/src/test/scala/org/specs2/guide/ForLoops.scala b/guide/src/test/scala/org/specs2/guide/ForLoops.scala index 45d351dad4..efc97e5d66 100644 --- a/guide/src/test/scala/org/specs2/guide/ForLoops.scala +++ b/guide/src/test/scala/org/specs2/guide/ForLoops.scala @@ -8,44 +8,43 @@ import org.specs2.fp.syntax.* object ForLoops extends UserGuidePage { def is = s2""" -It is very tempting to use `foreach` to create examples or results from a sequence of values:${snippet { - (1 to 3).foreach(i => "example " + i ! { i must ===(i) }) - }} +It is very tempting to use `foreach` to create examples or results from a sequence of values:${snippet{ +(1 to 3).foreach(i => "example " + i ! { i must ===(i) }) +}} The problem with `foreach` is that the return value of the expression above is `Unit`. So you won't be able to use it in an acceptance specification or a mutable one. ### A list of examples -When we want to create a list of examples we need to return a `Fragments` object. The long-winded way to do so is to use a `foldLeft`:${snippet { - (1 to 3).foldLeft(Fragments.empty)((res, i) => res.append("example " + i ! { i must ===(i) })) - }} +When we want to create a list of examples we need to return a `Fragments` object. The long-winded way to do so is to use a `foldLeft`:${snippet{ +(1 to 3).foldLeft(Fragments.empty)((res, i) => res.append("example " + i ! { i must ===(i) })) +}} -Or, a bit fancier with `foldMap`:${snippet { +Or, a bit fancier with `foldMap`:${snippet{ // Fragments has a Monoid instance so you can use the foldMap method - (1 to 3).toList.foldMap(i => Fragments("example " + i ! { i must ===(i) })) - }} +(1 to 3).toList.foldMap(i => Fragments("example " + i ! { i must ===(i) })) +}} -Because this is a recurring pattern there are two methods encapsulating it:${snippet { +Because this is a recurring pattern there are two methods encapsulating it:${snippet{ // when the function only returns a Fragment - Fragment.foreach(1 to 3)(i => "example " + i ! { i must ===(i) }): Fragments +Fragment.foreach(1 to 3)(i => "example " + i ! { i must ===(i) }): Fragments // when the function returns a Fragments object - Fragments.foreach(1 to 3) { i => - "examples for " + i ^ br ^ - "1 + " + i ! { (1 + i) must ===((i + 1)) } ^ br ^ - "2 + " + i ! { (2 + i) must ===((i + 2)) } - }: Fragments - }} - -Now you can create a list of examples inside a "should" block in a mutable specification:${snippet { - - class MySpec extends mutable.Specification: - "this block should have lots of examples" >> { - Fragment.foreach(1 to 1000) { i => - "example " + i ! { i must ===(i) } - } - } - }} +Fragments.foreach(1 to 3) { i => + "examples for " + i ^ br ^ + "1 + " + i ! { (1 + i) must ===((i + 1)) } ^ br ^ + "2 + " + i ! { (2 + i) must ===((i + 2)) } +}: Fragments +}} + +Now you can create a list of examples inside a "should" block in a mutable specification:${snippet{ +class MySpec extends mutable.Specification: + "this block should have lots of examples" >> { + Fragment.foreach(1 to 1000) { i => + "example " + i ! { i must ===(i) } + } + } +}} ### A list of expectations @@ -54,38 +53,38 @@ Similarly, when you want to create a list of expectations inside an example, you - If you need "thrown expectations", use the `foreach` and `forall` methods of `mutable.Specification`: $p -${snippet { - class MySpec extends mutable.Specification: - "this collects results of all expectations and throws an exception" >> { - foreach(1 to 10) { i => - i === 2 - } // Collects results of all expectations. Throws an exception. - foreach(1 to 10) { i => - i === i - } // This is not executed. - } - "this stops after the first failed expectation and throws an exception" >> { - forall(1 to 10) { i => - i === 2 - } // Stops after the first failed expectation. Throws an exception. - } - }} +${snippet{ +class MySpec extends mutable.Specification: + "this collects results of all expectations and throws an exception" >> { + foreach(1 to 10) { i => + i === 2 + } // Collects results of all expectations. Throws an exception. + foreach(1 to 10) { i => + i === i + } // This is not executed. + } + "this stops after the first failed expectation and throws an exception" >> { + forall(1 to 10) { i => + i === 2 + } // Stops after the first failed expectation. Throws an exception. + } +}} - If you need "functional expectations" that return a `Result`, use `Result.foreach` or `Result.forall`: -${snippet { - class MySpec extends mutable.Specification: - "this collects results of all expectations and returns a Result" >> { - Result.forall(1 to 10) { i => - i === 2 - } - } - "this stops after the first failed expectation and returns a Result" >> { - Result.foreach(1 to 10) { i => - i === 2 - } - } - }} +${snippet{ +class MySpec extends mutable.Specification: + "this collects results of all expectations and returns a Result" >> { + Result.forall(1 to 10) { i => + i === 2 + } + } + "this stops after the first failed expectation and returns a Result" >> { + Result.foreach(1 to 10) { i => + i === 2 + } + } +}} $AndIfYouWantToKnowMore diff --git a/guide/src/test/scala/org/specs2/guide/FragmentsApi.scala b/guide/src/test/scala/org/specs2/guide/FragmentsApi.scala index db5698c89d..341c4e83cf 100644 --- a/guide/src/test/scala/org/specs2/guide/FragmentsApi.scala +++ b/guide/src/test/scala/org/specs2/guide/FragmentsApi.scala @@ -40,12 +40,11 @@ Please have a look at the ScalaDoc to see the exact API for the factory and look ### The `Fragments` API -If you know how to create examples, texts and steps you will need to append them together as `Fragments`. You can create a `Fragments` object by using `Fragments.apply`:${snippet { - val ff = fragmentFactory - - Fragments(ff.text("introduction"), ff.example("first example", success), ff.break) - }} +If you know how to create examples, texts and steps you will need to append them together as `Fragments`. You can create a `Fragments` object by using `Fragments.apply`:${snippet{ +val ff = fragmentFactory +Fragments(ff.text("introduction"), ff.example("first example", success), ff.break) +}} Then you can use the methods of the `org.specs2.specification.core.Fragments` class to add more fragments or to modify existing ones: @@ -61,19 +60,19 @@ Then you can use the methods of the `org.specs2.specification.core.Fragments` cl ### The `Fragments` DSL -The `org.specs2.specification.dsl.FragmentsDsl` trait provides a very versatile `^` operator to append fragments together, so you can write:${snippet { - val ff = fragmentFactory +The `org.specs2.specification.dsl.FragmentsDsl` trait provides a very versatile `^` operator to append fragments together, so you can write:${snippet{ +val ff = fragmentFactory - val fs = Fragments(ff.text("introduction"), ff.example("first example", success), ff.break) - val f1 = ff.text("f1") +val fs = Fragments(ff.text("introduction"), ff.example("first example", success), ff.break) +val f1 = ff.text("f1") // all those combinations are possible and return a `Fragment` object - fs ^ f1 - f1 ^ fs - fs ^ fs - f1 ^ f1 +fs ^ f1 +f1 ^ fs +fs ^ fs +f1 ^ f1 - }} +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/GetAllExpectations.scala b/guide/src/test/scala/org/specs2/guide/GetAllExpectations.scala index ae3814aee5..070cd1606d 100644 --- a/guide/src/test/scala/org/specs2/guide/GetAllExpectations.scala +++ b/guide/src/test/scala/org/specs2/guide/GetAllExpectations.scala @@ -13,22 +13,22 @@ $p They correspond to different style of declaring expectations and you will use one or the other depending on how many expectations you have per example. The `org.specs2.specification.AllExpectations` trait goes further and gives you the possibility to report _all_ the failures of an Example without stopping at the first failure. -This enables a type of specification where it is possible to define lots of expectations inside the body of an example and get a maximum of information on what fails and what passes: ${snippet { - import org.specs2.specification.AllExpectations - import org.specs2.mutable.Specification - - class AllExpectationsSpec extends Specification with AllExpectations: - "In this example all the expectations are evaluated" >> { - 1 === 2 // this fails - 1 === 3 // this also fails - 1 === 1 - } - "There is no collision with this example" >> { - 10 === 11 // this fails - 12 === 12 - 13 === 31 // this also fails - } - }} +This enables a type of specification where it is possible to define lots of expectations inside the body of an example and get a maximum of information on what fails and what passes: ${snippet{ +import org.specs2.specification.AllExpectations +import org.specs2.mutable.Specification + +class AllExpectationsSpec extends Specification with AllExpectations: + "In this example all the expectations are evaluated" >> { + 1 === 2 // this fails + 1 === 3 // this also fails + 1 === 1 + } + "There is no collision with this example" >> { + 10 === 11 // this fails + 12 === 12 + 13 === 31 // this also fails + } +}} The second example above hints at a restriction for this kind of Specification. The failures are accumulated for each example by mutating a shared variable. "Mutable" means that the concurrent execution of examples will be an issue if done blindly. To avoid this, the `AllExpectations` trait overrides the `Specification` arguments to make it ${"sequential" ~/ Execution} (unless it already is). diff --git a/guide/src/test/scala/org/specs2/guide/HowTo.scala b/guide/src/test/scala/org/specs2/guide/HowTo.scala index 02dcf4dd79..193c360401 100644 --- a/guide/src/test/scala/org/specs2/guide/HowTo.scala +++ b/guide/src/test/scala/org/specs2/guide/HowTo.scala @@ -10,22 +10,16 @@ How to execute an action before all the examples? How to execute an action before each example? | ${link(Contexts).mute} Can I create and reuse a Specification "template"? | ${link(SpecificationTemplate).mute} How can I pass some data to each example? | ${link(Contexts).mute} -Can I add more information to my results for easier diagnostic? | ${link( - ExpectationDescription - ).mute} +Can I add more information to my results for easier diagnostic? | ${link(ExpectationDescription).mute} Is it possible to execute a Specification sequentially? | ${link(Execution).mute} -How to mark an example as `pending` until it is fixed? | ${link( - PendingUntilFixedExamples - ).mute} +How to mark an example as `pending` until it is fixed? | ${link(PendingUntilFixedExamples).mute} Can I simply mark a block of code as `pending`? | ${link(StandardResults).mute} Is there a way to run only one example? | ${link(Selection).mute} I would like to display the execution time of each example | ${link(ConsoleOutput).mute} Can I use a for loop to create examples or results? | ${link(ForLoops).mute} How can I tag examples? | ${link(Selection).mute} Can I skip examples? | ${link(SkipExamples).mute} -Is it possible to modify the behaviour of a Specification with command-line arguments? | ${link( - UseCommandLineArguments - ).mute} +Is it possible to modify the behaviour of a Specification with command-line arguments? | ${link(UseCommandLineArguments).mute} How to capture snippets of code and add them to my Specification? | ${link(CaptureSnippets).mute} Can I create an HTML index of all the specifications? | ${link(HtmlOutput).mute} Is it possible to execute a Specification in a random order? | ${link(RandomExecution).mute} @@ -40,7 +34,6 @@ Can I use parts of an example description in the example itself? Add my own keywords (other than '>>', 'should', 'can') to a mutable specification | ${link(AddKeywords).mute} Help, I need to troubleshoot my issues! | ${link(Troubleshooting).mute} How can I integrate my own IO type to specifications? | ${link(IoIntegration).mute} - """ def sortedLinks = diff --git a/guide/src/test/scala/org/specs2/guide/HtmlOutput.scala b/guide/src/test/scala/org/specs2/guide/HtmlOutput.scala index 19e45c4bda..a031eff324 100644 --- a/guide/src/test/scala/org/specs2/guide/HtmlOutput.scala +++ b/guide/src/test/scala/org/specs2/guide/HtmlOutput.scala @@ -78,21 +78,21 @@ Custom CSS and JavaScript files can be used without changing the template. In or ## Create an index -Here is something you can do to automatically create an index page for your specifications: ${snippet { +Here is something you can do to automatically create an index page for your specifications: ${snippet{ - import org.specs2.* - import specification.core.* - import runner.SpecificationsFinder.default.* +import org.specs2.* +import specification.core.* +import runner.SpecificationsFinder.default.* - class index extends Specification: - def is = - examplesLinks("Example specifications") +class index extends Specification: + def is = + examplesLinks("Example specifications") - // see the SpecificationsFinder trait for the parameters of the 'specifications' method - def examplesLinks(t: String) = - t.title ^ - Fragments.foreach(specifications())(s => link(s) ^ br) - }} + // see the SpecificationsFinder trait for the parameters of the 'specifications' method + def examplesLinks(t: String) = + t.title ^ + Fragments.foreach(specifications())(s => link(s) ^ br) +}} The specification above creates an index.html file in the `target/specs2-reports` directory. The specifications method creates specifications using the following parameters: @@ -115,10 +115,11 @@ class HtmlExampleSpec extends Specification { This is a specification to check the 'Hello world' string The 'Hello world' string should - contain 11 characters $e1 - start with 'Hello' $e2 - end with 'world' $e3 - """ + contain 11 characters $e1 + start with 'Hello' $e2 + end with 'world' $e3 + + """ def e1 = "Hello world" must haveSize(11) def e2 = "Hello world" must startWith("Hello") diff --git a/guide/src/test/scala/org/specs2/guide/LightweightSpecs.scala b/guide/src/test/scala/org/specs2/guide/LightweightSpecs.scala index 20ebc1e4e6..68753f3897 100644 --- a/guide/src/test/scala/org/specs2/guide/LightweightSpecs.scala +++ b/guide/src/test/scala/org/specs2/guide/LightweightSpecs.scala @@ -14,9 +14,9 @@ There at least 2 downsides to this approach: It is thus possible to use another class, `org.specs2.Spec` (or `org.specs2.mutable.Spec`), which only provides the minimum number of implicits to create specifications. -With the `Spec` class you can create examples and expectations with simple matchers. For example:${snippet { - class HelloWorldSpec extends Spec: - def is = s2""" +With the `Spec` class you can create examples and expectations with simple matchers. For example:${snippet{ +class HelloWorldSpec extends Spec: + def is = s2""" This is a specification to check the 'Hello world' string @@ -27,28 +27,28 @@ With the `Spec` class you can create examples and expectations with simple match """ - def e1 = "Hello world" must haveSize(11) - def e2 = "Hello world" must startWith("Hello") - def e3 = "Hello world" must endWith("world") - }} - -Or, for mutable specs:${snippet { - class HelloWorldSpec extends mutable.Spec: - - addParagraph("This is a specification to check the 'Hello world' string") - - "The 'Hello world' string should" >> { - "contain 11 characters" >> { - "Hello world" must haveSize(11) - } - "start with 'Hello'" >> { - "Hello world" must startWith("Hello") - } - "end with 'world'" >> { - "Hello world" must endWith("world") - } + def e1 = "Hello world" must haveSize(11) + def e2 = "Hello world" must startWith("Hello") + def e3 = "Hello world" must endWith("world") +}} + +Or, for mutable specs:${snippet{ + class HelloWorldSpec extends mutable.Spec: + + addParagraph("This is a specification to check the 'Hello world' string") + + "The 'Hello world' string should" >> { + "contain 11 characters" >> { + "Hello world" must haveSize(11) + } + "start with 'Hello'" >> { + "Hello world" must startWith("Hello") + } + "end with 'world'" >> { + "Hello world" must endWith("world") } - }} + } +}} If you compare those 2 specifications with the "HelloWorldSpec" examples using `Specification` you will notice some differences: diff --git a/guide/src/test/scala/org/specs2/guide/Matchers.scala b/guide/src/test/scala/org/specs2/guide/Matchers.scala index b83d97689b..b78441a69b 100644 --- a/guide/src/test/scala/org/specs2/guide/Matchers.scala +++ b/guide/src/test/scala/org/specs2/guide/Matchers.scala @@ -12,16 +12,16 @@ object Matchers extends UserGuidePage with Forms { The most frequent way to specify some expected behaviour with $specs2 is to use _matchers_. You generally execute an action, a command or a function and then check if the actual value you get is equal to an expected one (the ["arrange-act-assert"](http://bit.ly/arrange_act_assert) paradigm). -For example, if you create a specification for an object manipulating paths:${snippet { +For example, if you create a specification for an object manipulating paths:${snippet{ // 8<--- - object Paths { def directoryPath(p: String) = p + "/" } +object Paths { def directoryPath(p: String) = p + "/" } // 8<--- // describe the functionality - s2"the directoryPath method should return well-formed paths $e1" +s2"the directoryPath method should return well-formed paths $e1" // give an example with some code - def e1 = Paths.directoryPath("/tmp/path/to/dir") must beEqualTo("/tmp/path/to/dir/") - }} +def e1 = Paths.directoryPath("/tmp/path/to/dir") must beEqualTo("/tmp/path/to/dir/") +}} The `must` operator takes the actual value returned by `directoryPath` and applies it to a `Matcher` built with the expected value. `beEqualTo` is one of the many matchers defined by $specs2, it just checks if 2 values are equal. @@ -66,108 +66,101 @@ ${OptionalLanguageMatcherCards.toTabs} The easiest way to create a new matcher is to derive it from an existing one. You can: - * use logical operators ${snippet { - + * use logical operators ${snippet{ def beBetween(i: Int, j: Int) = be_>=(i) and be_<=(j) - }} - * "adapt" the actual value ${snippet { + * "adapt" the actual value ${snippet{ // This matcher adapts the existing `be_<=` matcher to a matcher applicable to `Any` - def beShort1 = be_<=(5) ^^ { (t: Any) => t.toString.length } +def beShort1 = be_<=(5) ^^ { (t: Any) => t.toString.length } // you can use aka to provide some information about the original value, before adaptation - def beShort2 = be_<=(5) ^^ { (t: Any) => t.toString.length aka "the string size" } +def beShort2 = be_<=(5) ^^ { (t: Any) => t.toString.length aka "the string size" } // The adaptation can also be done the other way around when it's more readable - def haveExtension(extension: =>String) = ((_: File).getPath) ^^ endWith(extension) - }} +def haveExtension(extension: =>String) = ((_: File).getPath) ^^ endWith(extension) +}} * adapt the actual and expected values. This matcher compares 2 `Human` objects but set their `wealth` field to 0 - so that the equals method will not fail on that field: ${snippet { + so that the equals method will not fail on that field: ${snippet{ - def beMostlyEqualTo(h: Human) = be_==(h) ^^^ ((_: Human).copy(wealth = 0)) - // then - Human(age = 20, wealth = 1000) must beMostlyEqualTo(Human(age = 20, wealth = 1)) // success - }} +def beMostlyEqualTo(h: Human) = be_==(h) ^^^ ((_: Human).copy(wealth = 0)) +// then +Human(age = 20, wealth = 1000) must beMostlyEqualTo(Human(age = 20, wealth = 1)) // success +}} - * use `eventually` to try a matcher a number of times until it succeeds: ${snippet { + * use `eventually` to try a matcher a number of times until it succeeds: ${snippet{ - val iterator = List(1, 2, 3).iterator +val iterator = List(1, 2, 3).iterator // Use eventually(retries, n.millis) to specify the number of tries and waiting time - iterator.next must be_==(3).eventually - }} - - * use `await` to create a matcher that will match on `Matcher[Future[T]]` (this requires an ${"execution environment" ~/ ExecutionEnvironments}): ${snippet { - // 8<-- - import scala.concurrent.* - import scala.concurrent.duration.* - given ee: ExecutionEnv = ??? - // 8<-- - Future(1) must be_>(0).await - Future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis) - }} - - * use `when` or `unless` to apply a matcher only if a condition is satisfied: ${snippet { - - 1 must be_==(2).when(false) // will return a success - 1 must be_==(2).unless(true) // same thing - - 1 must be_==(2).when(false, "don't check this") // will return a success - 1 must be_==(2).unless(true, "don't check this") // same thing - }} - - * use `iff` to say that a matcher must succeed if and only if a condition is satisfied: ${snippet { - - 1 must be_==(1).iff(true) // will return a success - 1 must be_==(2).iff(true) // will return a failure - 1 must be_==(2).iff(false) // will return a success - 1 must be_==(1).iff(false) // will return a failure - }} - - * use `orSkip` to return a `Skipped` result instead of a Failure if the condition is not satisfied ${snippet { - - 1 must be_==(2).orSkip - 1 must be_==(2).orSkip("Precondition failed") // prints "Precondition failed: '1' is not equal to '2'" - 1 must be_==(2).orSkip((ko: String) => "BAD " + ko) // prints "BAD '1' is not equal to '2'" - }} - - * use `orPending` to return a `Pending` result instead of a Failure if the condition is not satisfied ${snippet { - - 1 must be_==(2).orPending - 1 must be_==(2).orPending("Precondition failed") // prints "Precondition failed: '1' is not equal to '2'" - 1 must be_==(2).orPending((ko: String) => "BAD " + ko) // prints "BAD '1' is not equal to '2'" - }} +iterator.next must be_==(3).eventually +}} + + * use `await` to create a matcher that will match on `Matcher[Future[T]]` (this requires an ${"execution environment" ~/ ExecutionEnvironments}): ${snippet{ +// 8<-- +import scala.concurrent.* +import scala.concurrent.duration.* +given ee: ExecutionEnv = ??? +// 8<-- +Future(1) must be_>(0).await +Future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis) +}} + + * use `when` or `unless` to apply a matcher only if a condition is satisfied: ${snippet{ +1 must be_==(2).when(false) // will return a success +1 must be_==(2).unless(true) // same thing + +1 must be_==(2).when(false, "don't check this") // will return a success +1 must be_==(2).unless(true, "don't check this") // same thing +}} + + * use `iff` to say that a matcher must succeed if and only if a condition is satisfied: ${snippet{ +1 must be_==(1).iff(true) // will return a success +1 must be_==(2).iff(true) // will return a failure +1 must be_==(2).iff(false) // will return a success +1 must be_==(1).iff(false) // will return a failure +}} + + * use `orSkip` to return a `Skipped` result instead of a Failure if the condition is not satisfied ${snippet{ +1 must be_==(2).orSkip +1 must be_==(2).orSkip("Precondition failed") // prints "Precondition failed: '1' is not equal to '2'" +1 must be_==(2).orSkip((ko: String) => "BAD " + ko) // prints "BAD '1' is not equal to '2'" +}} + + * use `orPending` to return a `Pending` result instead of a Failure if the condition is not satisfied ${snippet{ +1 must be_==(2).orPending +1 must be_==(2).orPending("Precondition failed") // prints "Precondition failed: '1' is not equal to '2'" +1 must be_==(2).orPending((ko: String) => "BAD " + ko) // prints "BAD '1' is not equal to '2'" +}} ### Create your own -The easiest way to create a new matcher is to create it from a function returning a tuple with a boolean and one or more messages: ${snippet { +The easiest way to create a new matcher is to create it from a function returning a tuple with a boolean and one or more messages: ${snippet{ // import the necessary implicit conversions if you are outside of a Specification // import org.specs2.matcher.Matcher.{given} // annotate the return type so that implicit conversions can transform your function into a Matcher object // here just return a boolean and a failure message - def startWithHello: Matcher[String] = { (s: String) => - (s.startsWith("hello"), s + " doesn't start with hello") - } - - }} +def startWithHello: Matcher[String] = { (s: String) => + (s.startsWith("hello"), s + " doesn't start with hello") +} +}} -If you want absolute power over matching, you can define your own matcher extending `Matcher`: ${snippet { +If you want absolute power over matching, you can define your own matcher extending `Matcher`: ${snippet{ - import org.specs2.execute.Result.* +import org.specs2.execute.Result.* - case class BeMyOwnEmpty() extends Matcher[String] { - def apply[S <: String](s: Expectable[S]) = { - result(s.value.isEmpty, s.description + " is not empty") - } - } +case class BeMyOwnEmpty() extends Matcher[String] { + def apply[S <: String](s: Expectable[S]) = { + result(s.value.isEmpty, s.description + " is not empty") + } +} - "" must BeMyOwnEmpty() - }} +"" must BeMyOwnEmpty() +}} In the code above you have to: diff --git a/guide/src/test/scala/org/specs2/guide/MultilineDescriptions.scala b/guide/src/test/scala/org/specs2/guide/MultilineDescriptions.scala index ee5ecbcaca..50591ec40a 100644 --- a/guide/src/test/scala/org/specs2/guide/MultilineDescriptions.scala +++ b/guide/src/test/scala/org/specs2/guide/MultilineDescriptions.scala @@ -4,16 +4,16 @@ package guide object MultilineDescriptions extends UserGuidePage { def is = s2""" -In a `s2` string the description of an example is taken as all the text having the same indentation before the example body:${snippet { - s2""" - This is the introduction paragraph - Which presents the examples - the first example has one line $ok +In a `s2` string the description of an example is taken as all the text having the same indentation before the example body:${snippet{ +s2""" +This is the introduction paragraph +Which presents the examples + the first example has one line $ok - the second example has - more than one line $ok + the second example has + more than one line $ok """ - }} +}} This prints ``` @@ -25,13 +25,13 @@ Which presents the examples more than one line ``` -If you want the example description to be unevenly aligned you can use a margin `|`:${snippet { - s2""" - This is the introduction paragraph - Which presents the examples - |this example has a very - | very very - | specific indentation $ok +If you want the example description to be unevenly aligned you can use a margin `|`:${snippet{ +s2""" +This is the introduction paragraph +Which presents the examples + |this example has a very + | very very + | specific indentation $ok """ }} @@ -43,7 +43,5 @@ Which presents the examples very very specific indentation ``` - - """ } diff --git a/guide/src/test/scala/org/specs2/guide/MutableSpecSyntax.scala b/guide/src/test/scala/org/specs2/guide/MutableSpecSyntax.scala index a8bc92c9ad..90e9693f95 100644 --- a/guide/src/test/scala/org/specs2/guide/MutableSpecSyntax.scala +++ b/guide/src/test/scala/org/specs2/guide/MutableSpecSyntax.scala @@ -3,53 +3,49 @@ package org.specs2.guide object MutableSpecSyntax extends UserGuidePage { def is = "Mutable specification syntax".title ^ s2""" -The $Structure page presents one syntax for declaring examples in a mutable specification:${snippet { - class MySpecification extends org.specs2.mutable.Specification { - "this is my specification" >> { - "where example 1 must be true" >> { - 1 must ===(1) - } - "where example 2 must be true" >> { - 2 must ===(2) - } - } +The $Structure page presents one syntax for declaring examples in a mutable specification:${snippet{ +class MySpecification extends org.specs2.mutable.Specification: + "this is my specification" >> { + "where example 1 must be true" >> { + 1 must ===(1) } - }} + "where example 2 must be true" >> { + 2 must ===(2) + } + } +}} -You can also use the `should/in` syntax:${snippet { - class MySpecification extends org.specs2.mutable.Specification { - "this is my specification" should { - "have one example" in { - 1 must ===(1) - } - "and another one" in { - 2 must ===(2) - } - } +You can also use the `should/in` syntax:${snippet{ +class MySpecification extends org.specs2.mutable.Specification: + "this is my specification" should { + "have one example" in { + 1 must ===(1) } - }} + "and another one" in { + 2 must ===(2) + } + } +}} $warn You might get clashes with should which can also be used to declare expectations on strings: ``` -class MySpecification extends org.specs2.mutable.Specification { +class MySpecification extends org.specs2.mutable.Specification: "this" should { "will not compile because should is overloaded" in { "a string" should not(beEmpty) } } -} ``` -The easiest work around in that case is to use must for the expectation:${snippet { - class MySpecification extends org.specs2.mutable.Specification { - "this" should { - "compile now" in { - "a string" must not(beEmpty) - } - } +The easiest work around in that case is to use must for the expectation:${snippet{ +class MySpecification extends org.specs2.mutable.Specification: + "this" should { + "compile now" in { + "a string" must not(beEmpty) } - }} + } +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/OtherBuildTools.scala b/guide/src/test/scala/org/specs2/guide/OtherBuildTools.scala index 4a397afe87..5660ad123b 100644 --- a/guide/src/test/scala/org/specs2/guide/OtherBuildTools.scala +++ b/guide/src/test/scala/org/specs2/guide/OtherBuildTools.scala @@ -9,20 +9,10 @@ However other build tools such as Maven and Gradle can be used too (please refer ### Maven With Maven you need to use the [Surefire](http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html) plugin and the `test` command. -You will need however to annotate your specification classes as JUnit tests (this requires the `specs2-junit` jar):${snippet { - import org.specs2.runner.JUnitRunner - import org.junit.runner.RunWith - - @RunWith(classOf[JUnitRunner]) - class MySpecification extends org.specs2.Specification: - def is = s2""" - Define your specification as usual here - """ - }}} +(make sure that both the `specs2-junit` and the `org.junit.platform.junit-platform-engine` jars on your classpath) ### Gradle -With Gradle you need to use the same `RunWith` annotation and the [`test`](http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.Test.html) task. -You can also follow the instructions in [this blog post](http://blog.mindcrime-ilab.de/2013/10/25/gradle-rocking-scala-specs2-tests) and create a task that will leverage the $specs2 ${"file runner" ~/ RunInShell}. This way you will avoid having to annotate the classes. +With Gradle the [`test`](http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.testing.Test.html) task will run your specification as a JUnit test suite. """ } diff --git a/guide/src/test/scala/org/specs2/guide/PendingUntilFixedExamples.scala b/guide/src/test/scala/org/specs2/guide/PendingUntilFixedExamples.scala index 6010902752..e4959cb6dc 100644 --- a/guide/src/test/scala/org/specs2/guide/PendingUntilFixedExamples.scala +++ b/guide/src/test/scala/org/specs2/guide/PendingUntilFixedExamples.scala @@ -4,20 +4,21 @@ package guide object PendingUntilFixedExamples extends UserGuidePage { def is = "Pending until fixed".title ^ s2""" -Some examples may be temporarily failing but you may not want the entire test suite to fail just for those examples. Instead of commenting them out and then forgetting about those examples when the code is fixed, you can append `pendingUntilFixed` to the example: ${snippet { +Some examples may be temporarily failing but you may not want the entire test suite to fail just for those examples. +Instead of commenting them out and then forgetting about those examples when the code is fixed, you can append `pendingUntilFixed` to the example: ${snippet{ - class SpecificationWithPendingExamples extends mutable.Spec: - "this example fails for now" >> pendingUntilFixed { - 1 must ===(2) - } +class SpecificationWithPendingExamples extends mutable.Spec: + "this example fails for now" >> pendingUntilFixed { + 1 must ===(2) + } - // or, with a more specific message - "this example fails for now" >> pendingUntilFixed("ISSUE-123") { - 1 must ===(2) - } - }} - -The example above will be reported as `Pending` until it succeeds. Then it is marked as a failure so that you can remember to remove the `pendingUntilFixed` marker. + // or, with a more specific message + "this example fails for now" >> pendingUntilFixed("ISSUE-123") { + 1 must ===(2) + } +}} +The example above will be reported as `Pending` until it succeeds. \ +Then it is marked as a failure so that you can remember to remove the `pendingUntilFixed` marker. """ } diff --git a/guide/src/test/scala/org/specs2/guide/PrintExecutionData.scala b/guide/src/test/scala/org/specs2/guide/PrintExecutionData.scala index bea7fc8cc7..b7500f04b2 100644 --- a/guide/src/test/scala/org/specs2/guide/PrintExecutionData.scala +++ b/guide/src/test/scala/org/specs2/guide/PrintExecutionData.scala @@ -16,12 +16,12 @@ object PrintExecutionData extends UserGuidePage { If an example returns returns a `Success` we just print the example name on the console but it can be interesting to also get some information about the data the example was executed with. In order to do that you can use the `updateExpected` -method and pass a non-empty string with your message: ${snippet { - "this is an obvious example" ! { - val i = 1 - (i must ===(1)).updateExpected("executed with " + i) - } - }} +method and pass a non-empty string with your message: ${snippet{ +"this is an obvious example" ! { + val i = 1 + (i must ===(1)).updateExpected("executed with " + i) +} +}} This will print on the console: ``` @@ -73,43 +73,45 @@ the same functionality is actually accessible with the [`showtimes` argument]($C ### With the example description More generally, you can both use the example description and the example body to display custom messages. -One way to do this is by taking the advantage of the fact that a `Specification` is just a stream of `Fragments`: ${snippet { - - import org.specs2.specification.core.* - import org.specs2.execute.* - import org.specs2.time.* - - object extras: - /** measure the execution time of a piece of code */ - def withTimer[T](t: =>T): (T, SimpleTimer) = - val timer = (new SimpleTimer).start - val result = t - (result, timer.stop) - - // extend each example in a Specification with a measured time message - extension (fs: Fragments) - def showTimes: Fragments = - fs.map { - case f if Fragment.isExample(f) => - f.updateResult { r => - val (result, timer) = withTimer(ResultExecution.execute(AsResult(r))) - result.updateExpected("Execution time for \"" + f.description.show + "\": " + timer.time) - } - case other => other +One way to do this is by taking the advantage of the fact that a `Specification` is just a stream of `Fragments`: ${snippet{ + +import org.specs2.specification.core.* +import org.specs2.execute.* +import org.specs2.time.* + +object extras: + /** measure the execution time of a piece of code */ + def withTimer[T](t: =>T): (T, SimpleTimer) = + val timer = (new SimpleTimer).start + val result = t + (result, timer.stop) + + // extend each example in a Specification with a measured time message + extension (fs: Fragments) + def showTimes: Fragments = + fs.map { + case f if Fragment.isExample(f) => + f.updateResult { r => + val (result, timer) = withTimer(ResultExecution.execute(AsResult(r))) + result.updateExpected("Execution time for \"" + f.description.show + "\": " + timer.time) } + case other => other + } - // example of use - class HelloWorldSpec extends Specification: - def is = s2""" +// example of use +import extras.* +class HelloWorldSpec extends Specification: + def is = s2""" This is a specification to check the 'Hello world' string The 'Hello world' string should contain 11 characters $$e1 start with 'Hello' $$e2 end with 'world' $$e3 - """.showTimes - }} + """.showTimes + +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/QuickStart.scala b/guide/src/test/scala/org/specs2/guide/QuickStart.scala index 76dda572b3..df0450d687 100644 --- a/guide/src/test/scala/org/specs2/guide/QuickStart.scala +++ b/guide/src/test/scala/org/specs2/guide/QuickStart.scala @@ -4,12 +4,12 @@ package guide object QuickStart extends UserGuidePage { def is = "Quick Start".title ^ s2""" -Follow the ${"installation" ~/ Installation} instructions and create the following specification in a file named `HelloWorldSpec.scala`: ${snippet { +Follow the ${"installation" ~/ Installation} instructions and create the following specification in a file named `HelloWorldSpec.scala`: ${snippet{ - import org.specs2.* +import org.specs2.* - class HelloWorldSpec extends Specification: - def is = s2""" +class HelloWorldSpec extends Specification: + def is = s2""" This is a specification to check the 'Hello world' string @@ -20,11 +20,11 @@ The 'Hello world' string should """ - def e1 = "Hello world" must haveSize(11) - def e2 = "Hello world" must startWith("Hello") - def e3 = "Hello world" must endWith("world") + def e1 = "Hello world" must haveSize(11) + def e2 = "Hello world" must startWith("Hello") + def e3 = "Hello world" must endWith("world") - }} +}} A $specs2 software specification is a Scala class extending `org.specs2.Specification` and declaring an `is` method. That method defines a `s2` interpolated string with some plain text describing what the system should do @@ -33,29 +33,29 @@ and some code with executable examples. #### Unit specifications The style of writing specifications above, with most of the text first, then executable examples, is unconventional. -You can, if you prefer, use an alternative style: ${snippet { +You can, if you prefer, use an alternative style: ${snippet{ // note the different import here - import org.specs2.mutable.* +import org.specs2.mutable.* - class HelloWorldSpec extends Specification: +class HelloWorldSpec extends Specification: - "This is a specification to check the 'Hello world' string".br + "This is a specification to check the 'Hello world' string".br - "The 'Hello world' string should" >> { - "contain 11 characters" >> { - "Hello world" must haveSize(11) - } + "The 'Hello world' string should" >> { + "contain 11 characters" >> { + "Hello world" must haveSize(11) + } - "start with 'Hello'" >> { - "Hello world" must startWith("Hello") - } + "start with 'Hello'" >> { + "Hello world" must startWith("Hello") + } - "end with 'world'" >> { - "Hello world" must endWith("world") - } - } - }} + "end with 'world'" >> { + "Hello world" must endWith("world") + } + } +}} Both specifications will produce the same output. diff --git a/guide/src/test/scala/org/specs2/guide/RandomExecution.scala b/guide/src/test/scala/org/specs2/guide/RandomExecution.scala index e0977f30e4..be38a07f6b 100644 --- a/guide/src/test/scala/org/specs2/guide/RandomExecution.scala +++ b/guide/src/test/scala/org/specs2/guide/RandomExecution.scala @@ -19,9 +19,9 @@ dependencies to your examples so that they will force them to be executed in a r one after the other. This randomization is only being done for examples in between steps so if you have steps inside the specification guaranteeing some kind of checkpoints during the execution, they will be preserved. -Let's see this on an example:${snippet { - class RandomSequentialSpec extends Specification: - def is = sequentialRandom ^ s2""" +Let's see this on an example:${snippet{ +class RandomSequentialSpec extends Specification: + def is = sequentialRandom ^ s2""" example1 $e1 example2 $e2 example3 $e3 @@ -31,14 +31,14 @@ Let's see this on an example:${snippet { example6 $e6 """ - def e1 = { "e1".pp; ok } - def e2 = { "e2".pp; ok } - def e3 = { "e3".pp; ok } + def e1 = { "e1".pp; ok } + def e2 = { "e2".pp; ok } + def e3 = { "e3".pp; ok } - def e4 = { "e4".pp; ok } - def e5 = { "e5".pp; ok } - def e6 = { "e6".pp; ok } - }} + def e4 = { "e4".pp; ok } + def e5 = { "e5".pp; ok } + def e6 = { "e6".pp; ok } +}} With such a specification you might see in the console: ``` diff --git a/guide/src/test/scala/org/specs2/guide/ReferenceOtherSpecifications.scala b/guide/src/test/scala/org/specs2/guide/ReferenceOtherSpecifications.scala index bdd52b8b1b..9acd7ab154 100644 --- a/guide/src/test/scala/org/specs2/guide/ReferenceOtherSpecifications.scala +++ b/guide/src/test/scala/org/specs2/guide/ReferenceOtherSpecifications.scala @@ -10,11 +10,11 @@ For some large projects, or to write documentation, you will need to structure y - "link" reference: an "executed" reference where the second specification will be executed and its status reported in the first one $p -Here is the DSL you will use for those 2 types of references:${snippet { - object FirstSpecification extends Specification: - def is = s2""" +Here is the DSL you will use for those 2 types of references:${snippet{ +object FirstSpecification extends Specification: + def is = s2""" - We can consider one example + We can consider one example ${1 must ===(1)}) And all these examples are also important so we need to know if they all pass @@ -23,22 +23,20 @@ Here is the DSL you will use for those 2 types of references:${snippet { Finally it is worth having a look at ${"this specification" ~/ ThirdSpecification}. """ - import org.specs2.specification.core.* - - object SecondSpecification extends Specification: - def is = s2""" +import org.specs2.specification.core.* - This spec contains lots of examples +object SecondSpecification extends Specification: + def is = s2""" + This spec contains lots of examples ${Fragment.foreach(1 to 100) { i => "example " + i ! ok }} - """ - - object ThirdSpecification extends Specification: - def is = s2""" + """ - This is the third specification with a simple example - this should pass $ok - """ - }} +object ThirdSpecification extends Specification: + def is = s2""" + This is the third specification with a simple example + this should pass $ok + """ +}} The syntax shown above to create references is using a string for the link alias and uses two operators: @@ -47,16 +45,14 @@ The syntax shown above to create references is using a string for the link alias `~` | a *`link` reference*. The referenced specification gets executed when the first one is `~/` | a *`see` reference*. The referenced specification doesn't get executed (`"$$FirstSpecification"` creates a *see* link as well) -Also, for better html rendering, you can add a tooltip:${snippet { +Also, for better html rendering, you can add a tooltip:${snippet{ // 8<-- - object OtherSpec extends Specification { def is = ok } +object OtherSpec extends Specification { def is = ok } // 8<-- - - class s extends Specification: - def is = s2""" - ${"alias".~/(OtherSpec, "tooltip")} +class s extends Specification: + def is = s2""" + ${"alias".~/(OtherSpec, "tooltip")} """ - }} Finally I'm also drawing your attention to the fact that you don't have to create your specifications as Scala classes but you can use simple objects as shown above. diff --git a/guide/src/test/scala/org/specs2/guide/RunInIDE.scala b/guide/src/test/scala/org/specs2/guide/RunInIDE.scala index 2922dff9a2..07c8992e82 100644 --- a/guide/src/test/scala/org/specs2/guide/RunInIDE.scala +++ b/guide/src/test/scala/org/specs2/guide/RunInIDE.scala @@ -6,7 +6,7 @@ object RunInIDE extends UserGuidePage { ### Intellij IDEA -[IntelliJ IDEA](http://www.jetbrains.com/idea/features/scala.html) is the IDE with the best $specs2 integration for now. You can: +[IntelliJ IDEA](https://github.com/jetbrains/intellij-scala) is the IDE with the best $specs2 integration for now. You can: * execute a specification by selecting its name and pressing `CTRL+SHIFT+F10` * execute a single example by selecting its description and pressing `CTRL+SHIFT+F10` @@ -15,20 +15,9 @@ object RunInIDE extends UserGuidePage { However passing arguments needs to be done through system properties for now. So if you need to use the `xonly` argument you need to pass `-Dspecs2.xonly`. -### ScalaIDE +### VSCode -There is no integration of $specs2 in [ScalaIDE](http://scala-ide.org) yet, but it is possible to execute specifications as JUnit tests:${snippet { - import org.specs2.runner.JUnitRunner - import org.junit.runner.RunWith - - @RunWith(classOf[JUnitRunner]) - class MySpecification extends org.specs2.Specification { - def is = s2""" - Define your specification as usual here ... -""" - } - }} - -[*some [tricks](http://code.google.com/p/specs/wiki/RunningSpecs#Run_your_specification_with_JUnit4_in_Eclipse) described on the specs website can still be useful there when ScalaIDE struggles to find the specification classes*] +$specs2 is integrated to the [`metals` language server](https://scalameta.org/metals), which means that any compiling specification in VSCode should be +adorned with a small `test` button to run it. """ } diff --git a/guide/src/test/scala/org/specs2/guide/Selection.scala b/guide/src/test/scala/org/specs2/guide/Selection.scala index 616a76fb3b..c4e49789ae 100644 --- a/guide/src/test/scala/org/specs2/guide/Selection.scala +++ b/guide/src/test/scala/org/specs2/guide/Selection.scala @@ -23,21 +23,20 @@ sbt> testOnly *MySpecification* -- ex "contains hello" sequential ### Use tags Tags can be used in a Specification to include or exclude some examples or a complete section of fragments from the -execution. Let's have a look at one example: ${snippet { - class TaggedSpecification extends Specification: - def is = s2""" - - this is some introductory text - and the first group of examples - example 1 $success ${tag("feature1", "unit")} - example 2 $success ${tag("integration")} - - and the second group of examples ${section("checkin")} - example 3 $success - example 4 $success ${section("checkin")} -""" +execution. Let's have a look at one example: ${snippet{ +class TaggedSpecification extends Specification: + def is = s2""" + + this is some introductory text + and the first group of examples + example 1 $success ${tag("feature1", "unit")} + example 2 $success ${tag("integration")} - }} + and the second group of examples ${section("checkin")} + example 3 $success + example 4 $success ${section("checkin")} + """ +}} In that specification we are defining several tags and sections: @@ -55,30 +54,31 @@ Armed with this, it is now easy to include or exclude portions of the specificat #### In a unit specification -A _unit_ specification will accept the same `tag` and `section` methods but the behavior will be slightly different: ${snippet { - - import org.specs2.mutable.* - - class TaggedSpecification extends Specification: - "this is some introductory text" >> { - "and the first group of examples" >> { - tag("feature 1", "unit") - "example 1" in success - "example 2" in success - } - } - section("checkin") - "and the second group of examples" >> { - "example 3" in success - "example 4" in success - } - section("checkin") - - "and the last group of examples" >> { - "example 5" in success tag "integration" - "example 6" in success - } section "slow" - }} +A _unit_ specification will accept the same `tag` and `section` methods but the behavior will be slightly different: ${snippet{ + +import org.specs2.mutable.* + +class TaggedSpecification extends Specification: + "this is some introductory text" >> { + "and the first group of examples" >> { + tag("feature 1", "unit") + "example 1" in success + "example 2" in success + } + } + + section("checkin") + "and the second group of examples" >> { + "example 3" in success + "example 4" in success + } + + section("checkin") + "and the last group of examples" >> { + "example 5" in success tag "integration" + "example 6" in success + } section "slow" +}} For that specification above, tags can be applied to fragments following them: @@ -99,41 +99,40 @@ But they can also be applied to fragments preceding them: ##### Automatic sections If you call `addSections` from inside the specification, each "block" will be surrounded by section tags having the same -name as the block text:${snippet { - import org.specs2.mutable.* - - class SectionsSpecification extends Specification: - addSections() - - "first section" >> { - "and the first group of examples" >> { - "example 1" in success - "example 2" in success - } - } - "second section" >> { - "example 3" in success - "example 4" in success - } - - "third section" >> { - "example 5" in success - "example 6" in success - } - }} +name as the block text:${snippet{ +import org.specs2.mutable.* + +class SectionsSpecification extends Specification: + addSections() + + "first section" >> { + "example 1" in success + "example 2" in success + } + + "second section" >> { + "example 3" in success + "example 4" in success + } + + "third section" >> { + "example 5" in success + "example 6" in success + } +}} If you want you can execute only example 3 and 4 by running `sbt> testOnly *SectionsSpecification -- include "second section"`. #### `Always` tag Some specifications need to have `Steps` which will always be included whatever tags are specified on the command line. -This is the case when defining a ${""""template" specification""" ~/ SpecificationTemplate} with setup/teardown steps: ${snippet { - trait DatabaseSpec extends Specification: - override def map(fs: =>Fragments) = - step(success("startDb")) ^ tag(AlwaysTag) ^ - fs ^ - step(success("cleanDb")) ^ tag(AlwaysTag) - }} +This is the case when defining a ${""""template" specification""" ~/ SpecificationTemplate} with setup/teardown steps: ${snippet{ +trait DatabaseSpec extends Specification: + override def map(fs: =>Fragments) = + step(success("startDb")) ^ tag(AlwaysTag) ^ + fs ^ + step(success("cleanDb")) ^ tag(AlwaysTag) +}} ### Select failed examples diff --git a/guide/src/test/scala/org/specs2/guide/SkipExamples.scala b/guide/src/test/scala/org/specs2/guide/SkipExamples.scala index e0e85e31ab..2739bab70d 100644 --- a/guide/src/test/scala/org/specs2/guide/SkipExamples.scala +++ b/guide/src/test/scala/org/specs2/guide/SkipExamples.scala @@ -3,21 +3,24 @@ package guide object SkipExamples extends UserGuidePage { def is = s2""" -The section on ${"standard results" ~/ StandardResults} already presents two methods for skipping examples. But this works for individual examples only. In some circumstances you might want to skip a whole specification. For example, your specification needs to access a web service in order to work and this service might not be available on all testing machines. You can skip this specification conditionally with the `skipAllIf` argument:${snippet { - class InactiveSpec extends Specification: - def is = skipAllIf(databaseIsDown) ^ s2""" +The section on ${"standard results" ~/ StandardResults} already presents two methods for skipping examples. +But this works for individual examples only. In some circumstances you might want to skip a whole specification. +For example, your specification needs to access a web service in order to work and this service might not be available on all testing machines. +You can skip this specification conditionally with the `skipAllIf` argument:${snippet{ +class InactiveSpec extends Specification: + def is = skipAllIf(databaseIsDown) ^ s2""" There is a list of customers in the database $e1 One of them is called Eric $e2 - """ - def e1 = database.getCustomers must not(beEmpty) - def e2 = database.getCustomers must contain((_: Customer).name === "Eric") - }} + def e1 = database.getCustomers must not(beEmpty) + def e2 = database.getCustomers must contain((_: Customer).name === "Eric") +}} There also is a version of `skipAllIf` which reads better for some conditions: `skipAllUnless`. """ + case class Customer(name: String) object database { def getCustomers: List[Customer] = ??? diff --git a/guide/src/test/scala/org/specs2/guide/SpecificationFormatting.scala b/guide/src/test/scala/org/specs2/guide/SpecificationFormatting.scala index 0f6f59a67d..36fed711ca 100644 --- a/guide/src/test/scala/org/specs2/guide/SpecificationFormatting.scala +++ b/guide/src/test/scala/org/specs2/guide/SpecificationFormatting.scala @@ -5,13 +5,13 @@ object SpecificationFormatting extends UserGuidePage { def is = s2""" Acceptance specifications are displayed in the console almost as they are in `.scala` files thanks to interpolated strings. However it is not obvious to know how to change the display of a unit specification. How do you add a new line after an example? After the specification title? How do you indent a group of examples a bit more? -First of all, you can always add a piece text by using the `txt` method on a `String`:${snippet { - class UnitSpec extends mutable.Specification: - """ - This is a long and important introduction to this specification. - The examples below show everything you can do with the system. +First of all, you can always add a piece text by using the `txt` method on a `String`:${snippet{ +class UnitSpec extends mutable.Specification: + """ + This is a long and important introduction to this specification. + The examples below show everything you can do with the system. """.txt - }} +}} Then if you want to add new lines you can use: @@ -19,17 +19,16 @@ Then if you want to add new lines you can use: - `p` ("paragraph") to make a new paragraph with a break before and 2 after $p -Texts, or blocks of examples can also get a special indentation by using the `tab` and `backtab` methods:${snippet { - class UnitSpec extends mutable.Specification { - """ -This is a long and important introduction to this specification. -""".txt - - """ -The examples below show everything you can do with the system. -""".txt.tab(3) // indent the text with 3 tabs compared to the previous text - } - }} +Texts, or blocks of examples can also get a special indentation by using the `tab` and `backtab` methods:${snippet{ +class UnitSpec extends mutable.Specification: + """ + This is a long and important introduction to this specification. + """.txt + + """ + The examples below show everything you can do with the system. + """.txt.tab(3) // indent the text with 3 tabs compared to the previous text +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/SpecificationTemplate.scala b/guide/src/test/scala/org/specs2/guide/SpecificationTemplate.scala index 5633f69beb..cdec0abdcf 100644 --- a/guide/src/test/scala/org/specs2/guide/SpecificationTemplate.scala +++ b/guide/src/test/scala/org/specs2/guide/SpecificationTemplate.scala @@ -7,15 +7,15 @@ object SpecificationTemplate extends UserGuidePage { def is = s2""" On the ${"Contexts" ~/ Contexts} page we saw that there is a way to define an action which will be executed before all examples with the `BeforeSpec` trait. -You actually might want to create your own trait extending `BeforeSpec` in order to reuse this action in more than one specification:${snippet { - trait DatabaseSetup extends BeforeSpec: - def beforeSpec = step(println("prepare database")) +You actually might want to create your own trait extending `BeforeSpec` in order to reuse this action in more than one specification:${snippet{ +trait DatabaseSetup extends BeforeSpec: + def beforeSpec = step(println("prepare database")) - class DatabaseSpecification1 extends Specification with DatabaseSetup: - def is = s2""" +class DatabaseSpecification1 extends Specification with DatabaseSetup: + def is = s2""" // do something with the database - """ - }} +""" +}} How does this work? The `BeforeSpec` trait overrides a method called `map` in the `SpecificationStructure` trait (a parent of `Specification`) and adds the fragments defined by `beforeSpec` before anything else in the specification: @@ -26,6 +26,7 @@ override def map(fs: =>Fragments): Fragments = super.map(fs).prepend(beforeSpec.append(fragmentFactory.markAs(AlwaysTag))) ``` + The `map` method is indeed called every time the specification returns the list of `Fragment`s defining it. You can leverage this method and define your own "Specification templates": - adding some text before/after a Specification diff --git a/guide/src/test/scala/org/specs2/guide/Specs2Tags.scala b/guide/src/test/scala/org/specs2/guide/Specs2Tags.scala index 056db459b1..81fe802449 100644 --- a/guide/src/test/scala/org/specs2/guide/Specs2Tags.scala +++ b/guide/src/test/scala/org/specs2/guide/Specs2Tags.scala @@ -17,26 +17,28 @@ trait Specs2Tags: allTags.map(filterPublished) def filterPublished(tags: List[VersionTag]): List[VersionTag] = - tags.filter(isGreaterThanVersion3).groupBy(_.major).map(_._2).map(_.sorted.last).toList.sorted + tags.filter(isGreaterThanVersion(4)).groupBy(_.major).map(_._2).map(_.sorted.last).toList.sorted - def isGreaterThanVersion3: VersionTag => Boolean = (tag: VersionTag) => - Ordering[DotNumber].gteq(tag.number, DotNumber(List(3))) + def isGreaterThanVersion(n: Int): VersionTag => Boolean = (tag: VersionTag) => + Ordering[DotNumber].gteq(tag.number, DotNumber(List(n))) object Specs2Tags extends Specs2Tags import Specs2Tags.* -class Specs2TagsSpec extends Specification { +class Specs2TagsSpec extends Specification: def is = s2""" ${filterPublished( - List("SPECS2-2.4.9", "SPECS2-3.9.3", "SPECS2-3.9.4", "SPECS2-4.10.0", "SPECS2-4.12.1").flatMap( - VersionTag.fromString - ) - ) === - List("SPECS2-3.9.4", "SPECS2-4.12.1").flatMap(VersionTag.fromString)} + List("SPECS2-3.9.4", + "SPECS2-4.10.0", + "SPECS2-4.12.1", + "SPECS2-5.0.0-RC-01", + "SPECS2-5.0.0-RC-10").flatMap(VersionTag.fromString)) === + List("SPECS2-4.12.1", + "SPECS2-5.0.0-RC-10").flatMap(VersionTag.fromString)} """ -} + case class VersionTag(number: DotNumber, timestamp: Option[String], commit: Option[String]): def major: Int = diff --git a/guide/src/test/scala/org/specs2/guide/StandardResults.scala b/guide/src/test/scala/org/specs2/guide/StandardResults.scala index f429a8dd2e..f215cbdb5f 100644 --- a/guide/src/test/scala/org/specs2/guide/StandardResults.scala +++ b/guide/src/test/scala/org/specs2/guide/StandardResults.scala @@ -8,16 +8,16 @@ The $specs2 ${see(QuickStart)} guide introduces matchers to create expectations ### Boolean results This is the simplest kind of result you can define for an expectation but also the least expressive! -It can be useful for simple expectations but a failure will give few information on what went wrong: ${snippet { - new mutable.Specification: - "this example is ok" >> { - 1 == 1 - } - "this one is not" >> { - // fails with 'the value is false'... - 1 == 2 - } - }} +It can be useful for simple expectations but a failure will give few information on what went wrong: ${snippet{ +new mutable.Specification: + "this example is ok" >> { + 1 == 1 + } + "this one is not" >> { + // fails with 'the value is false'... + 1 == 2 + } +}} Not only that but in unit specification no exception will be thrown so you need to `&&` and `||` operators to connect your assertions if they span several lines. @@ -45,27 +45,27 @@ If you already have some code for your example, adding `skipped` at the end to s - the code will be executed which will waste resources $p -What you want in that case in to skip the whole block:${snippet { - s2" this example *must* be skipped $e1" +What you want in that case in to skip the whole block:${snippet{ +s2" this example *must* be skipped $e1" - def e1 = skipped { - // whatever code is in there, it will not be executed and the result will be skipped - throw new Exception("uh-oh") - 1 === 1 - } - }} +def e1 = skipped { + // whatever code is in there, it will not be executed and the result will be skipped + throw new Exception("uh-oh") + 1 === 1 +} +}} ### Setting an example as Pending -Similarly you can mark the example as `Pending`:${snippet { - s2" this example is pending for now$e1" +Similarly you can mark the example as `Pending`:${snippet{ +s2" this example is pending for now$e1" - def e1 = pending { - // whatever code is in there, it will not be executed and the result will be pending - throw new Exception("uh-oh") - 1 === 1 - } - }} +def e1 = pending { + // whatever code is in there, it will not be executed and the result will be pending + throw new Exception("uh-oh") + 1 === 1 +} +}} ### Standard `MatchResults` diff --git a/guide/src/test/scala/org/specs2/guide/Structure.scala b/guide/src/test/scala/org/specs2/guide/Structure.scala index edba350d87..99d4a4edba 100644 --- a/guide/src/test/scala/org/specs2/guide/Structure.scala +++ b/guide/src/test/scala/org/specs2/guide/Structure.scala @@ -30,18 +30,18 @@ Both ways of writing specifications have advantages and drawbacks: ### Acceptance specification An acceptance specification extends `org.specs2.Specification` and defines the `is` method. You can implement this method -with an interpolated **`s2`** string: ${snippet { - class MySpecification extends org.specs2.Specification: - def is = s2""" +with an interpolated **`s2`** string: ${snippet{ +class MySpecification extends org.specs2.Specification: + def is = s2""" this is my specification where example 1 must be true $e1 where example 2 must be true $e2 """ - def e1 = 1 === 1 - def e2 = 2 === 2 - }} + def e1 = 1 === 1 + def e2 = 2 === 2 +}} The `s2` string contains the text of your specification as well as some references to methods (`e1` and `e2`) defining some code eventually evaluating to a `Result` (this can take many forms, from a simple Boolean, to a `Future[Result]`, or some value @@ -57,17 +57,17 @@ All the rest, `"this is my specification"`, is parsed as `Text` and is not execu ### Unit specification -A unit specification extends `org.specs2.mutable.Specification` and uses the `>>` operator to create "blocks" containing `Texts` and `Examples`: ${snippet { - class MySpecification extends org.specs2.mutable.Specification: - "this is my specification" >> { - "where example 1 must be true" >> { - 1 must ===(1) - } - "where example 2 must be true" >> { - 2 must ===(2) - } - } - }} +A unit specification extends `org.specs2.mutable.Specification` and uses the `>>` operator to create "blocks" containing `Texts` and `Examples`: ${snippet{ +class MySpecification extends org.specs2.mutable.Specification: + "this is my specification" >> { + "where example 1 must be true" >> { + 1 must ===(1) + } + "where example 2 must be true" >> { + 2 must ===(2) + } + } +}} This specification creates one piece of `Text` and 2 `Examples` as before but: @@ -92,37 +92,39 @@ The good news is that for each of the 2 main styles, acceptance and unit, you ca #### Functional expectations In an acceptance specification, by default, the `Result` of an `Example` is always given by the last statement of its body. -For instance, this example will never fail because the first expectation is lost:${snippet { +For instance, this example will never fail because the first expectation is lost:${snippet{ // this will never fail! - s2""" +s2""" my example on strings $e1 """ - def e1 = - // because this expectation will not be returned,... - "hello" must haveSize(10000) - "hello" must startWith("hell") - }} - -If you want to get both expectations you will need to use `and` between them: ${snippet { - s2""" + +def e1 = + // because this expectation will not be returned,... + "hello" must haveSize(10000) + "hello" must startWith("hell") +}} + +If you want to get both expectations you will need to use `and` between them: ${snippet{ +s2""" my example on strings $e1 """ - def e1 = ("hello" must haveSize(10000)) and - ("hello" must startWith("hell")) - }} +def e1 = + ("hello" must haveSize(10000)) and + ("hello" must startWith("hell")) +}} This is a bit tedious and not very pleasing to read so you can see why this mode encourages one expectation per example only! If you want to declare several expectations per example, you can mix-in the `org.specs2.matcher.ThrownExpectations` trait to the specification. #### Thrown expectations -With a unit specification you get "thrown expectations" by default. When an expectation fails, it throws an exception and the rest of the example is not executed: ${snippet { - class MySpecification extends org.specs2.mutable.Specification: - "This is my example" >> { - 1 === 2 // this fails - 1 === 1 // this is not executed - } - }} +With a unit specification you get "thrown expectations" by default. When an expectation fails, it throws an exception and the rest of the example is not executed: ${snippet{ +class MySpecification extends org.specs2.mutable.Specification: + "This is my example" >> { + 1 === 2 // this fails + 1 === 1 // this is not executed + } +}} It is also possible to use the "functional" expectation mode with a unit specification by mixing in the `org.specs2.matcher.NoThrownExpectations` trait. diff --git a/guide/src/test/scala/org/specs2/guide/TimeoutExamples.scala b/guide/src/test/scala/org/specs2/guide/TimeoutExamples.scala index a40c53b029..7b70a670ee 100644 --- a/guide/src/test/scala/org/specs2/guide/TimeoutExamples.scala +++ b/guide/src/test/scala/org/specs2/guide/TimeoutExamples.scala @@ -17,17 +17,16 @@ sbt> testOnly -- timeout 500 ### Specification timeout -It is also possible to specify a specification timeout overriding the global timeout by specifying the timeout argument: ${snippet { - class MySpecification extends Specification: - def is = args.execute(timeout = 10.seconds) ^ s2""" - this example should not take too long $e1 - this one too $e2 +It is also possible to specify a specification timeout overriding the global timeout by specifying the timeout argument: ${snippet{ +class MySpecification extends Specification: + def is = args.execute(timeout = 10.seconds) ^ s2""" + this example should not take too long $e1 + this one too $e2 """ - def e1 = { 1 + 1 === 2 } - def e2 = { 2 + 2 === 4 } - }} - + def e1 = { 1 + 1 === 2 } + def e2 = { 2 + 2 === 4 } +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/TroubleShooting.scala b/guide/src/test/scala/org/specs2/guide/TroubleShooting.scala index 8c8fd023e0..ce31867965 100644 --- a/guide/src/test/scala/org/specs2/guide/TroubleShooting.scala +++ b/guide/src/test/scala/org/specs2/guide/TroubleShooting.scala @@ -14,16 +14,16 @@ The common symptom here is a `NullPointerException` for some attributes of your ### Lost expectations -You might expect the following specification to fail:${snippet { - class ShouldItFail extends Specification: - def is = s2""" +You might expect the following specification to fail:${snippet{ +class ShouldItFail extends Specification: + def is = s2""" Should this example fail? $e1 """ - def e1 = - 1 must ===(100000) // do you expect this to fail - 10 must ===(10) - }} + def e1 = + 1 must ===(100000) // do you expect this to fail + 10 must ===(10) +}} However, as explained in ${see(Structure)} - Thrown expectations, the first expectation is lost because, by default, no exceptions are thrown in an acceptance specification. In that case you can either: diff --git a/guide/src/test/scala/org/specs2/guide/UseCommandLineArguments.scala b/guide/src/test/scala/org/specs2/guide/UseCommandLineArguments.scala index 8064cbec77..0e9d820831 100644 --- a/guide/src/test/scala/org/specs2/guide/UseCommandLineArguments.scala +++ b/guide/src/test/scala/org/specs2/guide/UseCommandLineArguments.scala @@ -15,32 +15,31 @@ without having to recompile it. ### Control an example -Let's see first how to use the command line to modify the outcome of just one example:${snippet { +Let's see first how to use the command line to modify the outcome of just one example:${snippet{ - import org.specs2.main.* +import org.specs2.main.* - class SpecificationWithArgs(args: CommandLine) extends Specification: - def is = s2""" - - This example is controlled from the command line $e1 - """ +class SpecificationWithArgs(args: CommandLine) extends Specification: + def is = s2""" + This example is controlled from the command line $e1 + """ - def e1 = - if (args.isSet("isOk")) - 1 must ===(1) - else - 1 must ===(2) + def e1 = + if args.isSet("isOk") then + 1 === 1 + else + 1 === 2 }} -With a mutable specification the code is similar:${snippet { - class SpecificationWithArgs(args: CommandLine) extends mutable.Spec: - "This example is controlled from the command line" >> { - if (args.isSet("isOk")) - 1 must ===(1) - else - 1 must ===(2) - } - }} +With a mutable specification the code is similar:${snippet{ +class SpecificationWithArgs(args: CommandLine) extends mutable.Spec: + "This example is controlled from the command line" >> { + if args.isSet("isOk") then + 1 === 1 + else + 1 === 2 + } +}} Then you can set the argument, or not in sbt with ``` @@ -61,23 +60,23 @@ Any specification with a 1-parameter constructor can be instantiated provided th $p In particular this means that you can define a `Specification` with a constructor using a `CommandLine` argument and when -the specification will be created it will be passed the command line arguments: ${snippet { +the specification will be created it will be passed the command line arguments: ${snippet{ // 8<--- - case class DbClient(env: String): - def createUser(name: String): Option[String] = ??? - trait DbSpec extends Specification: - lazy val client: DbClient = ??? +case class DbClient(env: String): + def createUser(name: String): Option[String] = ??? +trait DbSpec extends Specification: + lazy val client: DbClient = ??? // 8<--- - case class MyDbSpec(commandLine: CommandLine) extends Specification with DbSpec: - def is = s2""" +case class MyDbSpec(commandLine: CommandLine) extends Specification with DbSpec: + def is = s2""" create a user $createUser """ - // the database client is created from the command line - // arguments and can be used in the examples - def createUser = client.createUser("xxx") must beSome - }} + // the database client is created from the command line + // arguments and can be used in the examples + def createUser = client.createUser("xxx") must beSome +}} ``` // Template trait for accessing the database diff --git a/guide/src/test/scala/org/specs2/guide/UseDatatables.scala b/guide/src/test/scala/org/specs2/guide/UseDatatables.scala index 2120db6880..722f2ccc5e 100644 --- a/guide/src/test/scala/org/specs2/guide/UseDatatables.scala +++ b/guide/src/test/scala/org/specs2/guide/UseDatatables.scala @@ -12,30 +12,30 @@ import org.specs2.fp.syntax.* object UseDatatables extends UserGuidePage with Tables { def is = "Datatables".title ^ s2""" -DataTables are used to pack several expectations inside one example using a tabular format: ${snippet { +DataTables are used to pack several expectations inside one example using a tabular format: ${snippet{ - class DataTableSpec extends Specification with org.specs2.specification.Tables: - def is = s2""" +class DataTableSpec extends Specification with org.specs2.specification.Tables: + def is = s2""" - adding integers should just work in scala ${// the header of the table, with `|` separated strings (`>` executes the table) - "a" | "b" | "c" |> - 2 ! 2 ! 4 | // an example row - 1 ! 1 ! 2 | // another example row - { (a, b, c) => a + b must ===(c) } // the expectation to check on each row) - } -""" - }} - -A `DataTable` which is used as a `Result` in the body of an Example will only be displayed when failing. If you also want to display the table when successful, to document your examples, you can omit the example description and inline the DataTable directly in the specification:${snippet { - class DataTableSpec extends Specification with Tables: - def is = s2""" - - adding integers should just work in scala + adding integers should just work in scala ${// the header of the table, with `|` separated strings (`>` executes the table) + "a" | "b" | "c" |> + 2 ! 2 ! 4 | // an example row + 1 ! 1 ! 2 | // another example row + { (a, b, c) => a + b must ===(c) } // the expectation to check on each row) + } + """ +}} + +A `DataTable` which is used as a `Result` in the body of an Example will only be displayed when failing. If you also want to display the table when successful, to document your examples, you can omit the example description and inline the DataTable directly in the specification:${snippet{ +class DataTableSpec extends Specification with Tables: + def is = s2""" + + adding integers should just work in scala ${"a" | "b" | "c" |> - 2 ! 2 ! 4 | - 1 ! 1 ! 2 | { (a, b, c) => a + b must ===(c) }} -""" - }} + 2 ! 2 ! 4 | + 1 ! 1 ! 2 | { (a, b, c) => a + b must ===(c) }} + """ +}} This specification will be rendered as: ``` @@ -48,19 +48,19 @@ adding integers should just work in scala ### Format columns The display of elements can be modified by using an implicit `org.specs2.text.Showx` instance where `x` corresponds to -the number of columns in the table. For example: ${snippet { - import org.specs2.text.* +the number of columns in the table. For example: ${snippet{ +import org.specs2.text.* - given Show3[Int, Double, String] = - Show3[Int, Double, String]().copy(show2 = (d: Double) => "x" * d.toInt) +given Show3[Int, Double, String] = + Show3[Int, Double, String]().copy(show2 = (d: Double) => "x" * d.toInt) - val table = - "a" | "b" | "c" |> - 1 ! 2.0 ! "3" | - 2 ! 4.0 ! "6" | { (a: Int, b: Double, c: String) => (a + b.toInt).toString === c } +val table = + "a" | "b" | "c" |> + 1 ! 2.0 ! "3" | + 2 ! 4.0 ! "6" | { (a: Int, b: Double, c: String) => (a + b.toInt).toString === c } - "table result\n" + table.message - }.eval} +"table result\n" + table.message +}.eval} ### Implicit `!` @@ -75,32 +75,30 @@ $p ### Concurrent execution By default the execution of a datatable is sequential, one row after another. This might not be very practical if you have long-running computations on each row. -If this is the case you can use the `|*` operator (instead of just `|`) to define your execution function:${snippet { +If this is the case you can use the `|*` operator (instead of just `|`) to define your execution function:${snippet{ - given executionContext: ExecutionContext = - scala.concurrent.ExecutionContext.Implicits.global +given executionContext: ExecutionContext = + scala.concurrent.ExecutionContext.Implicits.global - "a" | "b" | "c" |> - 2 ! 2 ! 4 | - 1 ! 1 ! 2 |* { (a, b, c) => (a + b) must ===(c) } - }} +"a" | "b" | "c" |> + 2 ! 2 ! 4 | + 1 ! 1 ! 2 |* { (a, b, c) => (a + b) === c } +}} This returns a function `ExecutorService => Result` which can be used directly as the body of an example. You can also pass it your own thread pool by creating, for example, `java.util.concurrent.Executors.newFixedThreadPool(4)`. -More generally, you can use the "Applicative" operator `|@` to pass anything having a `org.specs2.fp.Applicative` instance, like a `scala.concurrent.Future`:${snippet { - // this table uses the global execution context implicitly to create futures - // scala.concurrent.ExecutionContext.Implicits.global - def result: scala.concurrent.Future[DecoratedResult[DataTable]] = - "a" | "b" | "c" |> - 2 ! 2 ! 4 | - 1 ! 1 ! 2 |@ { (a, b, c) => Future((a + b) must ===(c)) } - - // then you need to get an implicit execution environment and - // await on the Future result - given ExecutionEnv = ??? - result.await - }} - - +More generally, you can use the "Applicative" operator `|@` to pass anything having a `org.specs2.fp.Applicative` instance, like a `scala.concurrent.Future`:${snippet{ +// this table uses the global execution context implicitly to create futures +// scala.concurrent.ExecutionContext.Implicits.global +def result: scala.concurrent.Future[DecoratedResult[DataTable]] = + "a" | "b" | "c" |> + 2 ! 2 ! 4 | + 1 ! 1 ! 2 |@ { (a, b, c) => Future((a + b) must ===(c)) } + +// then you need to get an implicit execution environment and +// await on the Future result +given ExecutionEnv = ??? +result.await +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/UseForms.scala b/guide/src/test/scala/org/specs2/guide/UseForms.scala index ae515503a0..4a51e55c10 100644 --- a/guide/src/test/scala/org/specs2/guide/UseForms.scala +++ b/guide/src/test/scala/org/specs2/guide/UseForms.scala @@ -73,7 +73,7 @@ A `Prop` is like a `Field`, it has a label. But you can give it 2 values, an "ac If the matcher is `mute`d then no message will be displayed in case of a failure. -If the expected value is not provided when building the property, it can be given with the `apply` method:${snippet { +If the expected value is not provided when building the property, it can be given with the `apply` method:${snippet{ // 8<-- val prop1 = prop("actual") // 8<-- // apply "sets" the expected value @@ -154,7 +154,7 @@ Most of the time, the display of Fields and Properties can be left as it is but ### Simple form -Now that we know how to create Fields and Properties, creating a `Form` is as easy as putting them on separate lines: ${snippet { +Now that we know how to create Fields and Properties, creating a `Form` is as easy as putting them on separate lines: ${snippet{ Form("Address").tr(prop("street", actualStreet(123), "Oxford St")).tr(prop("number", actualNumber(123), 20)) }} @@ -167,15 +167,14 @@ In some cases (see the Calculator example below) you can create a header row usi * or `th("a", "b")` using an implicit conversion of Any => Field[Any] $p -Inserting the form in a Specification is also very simple: ${snippet { - class SpecificationWithForms extends Specification with Forms { - def is = s2""" +Inserting the form in a Specification is also very simple: ${snippet{ +class SpecificationWithForms extends Specification with Forms: + def is = s2""" -The address must be retrieved from the database with the proper street and number - ${Form("Address").tr(prop("street", actualStreet(123), "Oxford St")).tr(prop("number", actualNumber(123), 20))} -""" - } - }} + The address must be retrieved from the database with the proper street and number + ${Form("Address").tr(prop("street", actualStreet(123), "Oxford St")).tr(prop("number", actualNumber(123), 20))} + """ +}} One way to encapsulate and reuse this Form across specifications is to define a case class: ``` @@ -187,46 +186,45 @@ case class Address(street: String, number: Int): tr(prop("number", address.number, number)) ``` -And then you can use it like this: ${snippet { +And then you can use it like this: ${snippet{ // 8<-- - import Model.* +import Model.* // 8<-- - class AddressSpecification extends Specification with Forms: - def is = s2""" +class AddressSpecification extends Specification with Forms: + def is = s2""" - The address must be retrieved from the database with the proper street and number - ${Address("Oxford St", 20) + The address must be retrieved from the database with the proper street and number + ${Address("Oxford St", 20) . /** expected values */ retrieve(123) /** actual address id */} - """ - }} + """ +}} ##### Adding several rows at once -A very practical way to add rows programmatically is to start from a list of values and have a function creating a Row object for each value: ${snippet { - Form("a new Form").trs(addresses) { (a: ComponentsDefinitions.Address) => Row.tr(field(a.number), field(a.street)) } - }} +A very practical way to add rows programmatically is to start from a list of values and have a function creating a Row object for each value: ${snippet{ +Form("a new Form").trs(addresses) { (a: ComponentsDefinitions.Address) => Row.tr(field(a.number), field(a.street)) } +}} ${Form("a new Form").trs(addresses) { (a: ComponentsDefinitions.Address) => Row.tr(field(a.number), field(a.street)) }} #### Nesting into another Form -Forms can be composed of other Forms to display composite information: ${snippet { - val address = Form("Address").tr(field("street", "Rose Crescent")).tr(field("number", 3)) +Forms can be composed of other Forms to display composite information: ${snippet{ +val address = Form("Address").tr(field("street", "Rose Crescent")).tr(field("number", 3)) - val person = Form("Person").tr(field("name", "Eric")).tr(address) - }} +val person = Form("Person").tr(field("name", "Eric")).tr(address) +}} ${Form("Person").tr(field("name", "Eric"))} This will be displayed with the address as a nested table inside the main one on the last row. However in some case, it's preferable to have the rows of that Form to be included directly in the outer table. This can be done by *inlining* the -nesting Form: ${snippet { - - val person = Form("Person").tr(field("name", "Eric")).tr(address.inline) // address is inlined - }} +nesting Form: ${snippet{ +val person = Form("Person").tr(field("name", "Eric")).tr(address.inline) // address is inlined +}} And the result is: @@ -234,18 +232,17 @@ ${Form("Person").tr(field("name", "Eric")).tr(address.inline)} #### Nesting into an Effect or a Prop -When using Forms in specifications we can describe different levels of abstraction. If we consider the specification of a website for example, we want to be able to use a Form having 2 rows and describing the exact actions to do on the Login page: ${snippet { - - val loginForm = Form("login") - .tr(effect("click on login", clickOn("login"))) - .tr(effect("enter name", enter("name", "me"))) - .tr(effect("enter password", enter("password", "pw"))) - .tr(effect("submit", submit())) - }} +When using Forms in specifications we can describe different levels of abstraction. If we consider the specification of a website for example, we want to be able to use a Form having 2 rows and describing the exact actions to do on the Login page: ${snippet{ +val loginForm = Form("login") + .tr(effect("click on login", clickOn("login"))) + .tr(effect("enter name", enter("name", "me"))) + .tr(effect("enter password", enter("password", "pw"))) + .tr(effect("submit", submit())) +}} $loginForm However in a "purchase" scenario we want all the steps above to represent the login actions as just one step. One way to -do this is to transform the login Form to an Effect or a Prop: ${snippet { +do this is to transform the login Form to an Effect or a Prop: ${snippet{ Form("purchase") .tr(loginForm.toEffect("login")) @@ -276,32 +273,31 @@ ${Form("purchase") #### Using tabs -If there are too many fields to be displayed on a Form you can use tabs: ${snippet { +If there are too many fields to be displayed on a Form you can use tabs: ${snippet{ // 8<--- - import ComponentsDefinitions.* +import ComponentsDefinitions.* // 8<--- - s2""" +s2""" A person can have 2 addresses ${Form("Addresses").tr { tab("home", Address("Oxford St", 12).fill("Oxford St", 12)) .tab("work", Address("Rose Cr.", 3).fill("Rose Cr.", 3)) - }} +}} """ - }} +}} The first `tab` call will create a `Tabs` object containing the a first tab with "home" as the title and an Address form as its content. Then every subsequent `tab` calls on the `Tabs` object will create new tabs: ${Form("Addresses").tr( tab("home", Form("Address").tr(prop("street", "Oxford St")("Oxford St")).tr(prop("number", 12)(12))) - .tab("work", Form("Address").tr(prop("street", "Rose Cr.")("Rose Cr.")).tr(prop("number", 2)(2))) - )} + .tab("work", Form("Address").tr(prop("street", "Rose Cr.")("Rose Cr.")).tr(prop("number", 2)(2))))} -Tabs can also be created from a seq of values. Let's pretend we have a list of `Address` objects with a name and a Form displaying the `Address` values. You can write: ${snippet { +Tabs can also be created from a seq of values. Let's pretend we have a list of `Address` objects with a name and a Form displaying the `Address` values. You can write: ${snippet{ // 8<-- - import ComponentsDefinitions.* +import ComponentsDefinitions.* // 8<-- - Form("Addresses").tabs(addresses) { (address: Address) => tab(address.street, address.form) } - }} +Form("Addresses").tabs(addresses) { (address: Address) => tab(address.street, address.form) } +}} ### Aggregating forms @@ -330,47 +326,46 @@ case class Customer(name: String, address: Address): def actualCustomer(customerId: Int): Customer = this // fetch from the database ``` -${snippet { +${snippet{ // 8<-- - import Model.* +import Model.* // 8<-- - class CustomerSpecification extends Specification with Forms: - def is = s2""" +class CustomerSpecification extends Specification with Forms: + def is = s2""" The customer must be retrieved from the database with a proper name and address ${Customer( name = "Eric", address = Address(street = "Rose Crescent", number = 2) ).retrieve(123)} """ - }} +}} As you also see above, named arguments can bring more readability to the expected values. #### Lazy cells -Fields, Props and Forms are added right away to a row when building a Form with the `tr` method. If it is necessary to add them with a "call-by-name" behavior, the `lazify` method can be used: ${snippet { +Fields, Props and Forms are added right away to a row when building a Form with the `tr` method. If it is necessary to add them with a "call-by-name" behavior, the `lazify` method can be used: ${snippet{ // 8<-- - import Model.* +import Model.* // 8<-- - def address = Address() // build an Address - def customer = Customer() - - Form("Customer") - .tr(prop("name", customer.name)("name")) - . - // the address Form will be built only when the Customer Form is rendered - tr(lazify(address.actualIs(customer.address))) - }} +def address = Address() // build an Address +def customer = Customer() + +Form("Customer") + .tr(prop("name", customer.name)("name")) + . + // the address Form will be built only when the Customer Form is rendered + tr(lazify(address.actualIs(customer.address))) +}} #### Xml cells -Any xml can be "injected" on a row by using an `XmlCell`: ${snippet { +Any xml can be "injected" on a row by using an `XmlCell`: ${snippet{ // 8<-- - import Model.* +import Model.* // 8<-- - - def actualAddress(i: Int) = addresses(0) - Form("Customer").tr(prop("name", Customer().name)("name")).tr(XmlCell(

this is a bold statement
)) - }} +def actualAddress(i: Int) = addresses(0) +Form("Customer").tr(prop("name", Customer().name)("name")).tr(XmlCell(
this is a bold statement
)) +}} ### 1-n relationships @@ -419,7 +414,7 @@ It also has several methods to build Forms depending on the kind of comparison t * lines existing in `b` and not `a` are marked as failures $p -${snippet { +${snippet{ Order(123).hasSubset(OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) }} @@ -430,10 +425,7 @@ ${Order(123) OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5) - ) - .executeForm - .toXml - .toString} + ).executeForm.toXml.toString} #### Subsequence @@ -445,9 +437,9 @@ ${Order(123) * lines existing in `b` and `a` but out of order are marked as failures $p -${snippet { - Order(123).hasSubsequence(OrderLine("PS", 2), OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) - }} +${snippet{ +Order(123).hasSubsequence(OrderLine("PS", 2), OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) +}} This form returns: @@ -457,10 +449,7 @@ ${Order(123) OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5) - ) - .executeForm - .toXml - .toString} + ).executeForm.toXml.toString} #### Set @@ -471,9 +460,9 @@ ${Order(123) * lines existing in `b` and not `a` are marked as failures $p -${snippet { - Order(123).hasSet(OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) - }} +${snippet{ +Order(123).hasSet(OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) +}} This form returns: @@ -482,10 +471,7 @@ ${Order(123) OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5) - ) - .executeForm - .toXml - .toString} + ).executeForm.toXml.toString} #### Sequence @@ -495,9 +481,9 @@ ${Order(123) * lines existing in `a` and `b` in the right order are marked as success * lines existing in `b` and not `a` are marked as failures $p -${snippet { - Order(123).hasSequence(OrderLine("PS", 2), OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) - }} +${snippet{ +Order(123).hasSequence(OrderLine("PS", 2), OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5)) +}} This form returns: @@ -507,26 +493,23 @@ ${Order(123) OrderLine("BS", 3), OrderLine("PIS", 1), OrderLine("TDGL", 5) - ) - .executeForm - .toXml - .toString} + ).executeForm.toXml.toString} ### Decision tables One very popular type of Forms are *decision tables*. A decision table is a Form where, on each row, several values are used for a computation and the result must be equal to other values on the same row. -A very simple example of this is a calculator: ${snippet { - - case class Calculator(form: Form = Form()): - def tr(a: Int, b: Int, a_plus_b: Int, a_minus_b: Int) = Calculator { - def plus = prop(a + b)(a_plus_b) - def minus = prop(a - b)(a_minus_b) - form.tr(a, b, plus, minus) - } +A very simple example of this is a calculator: ${snippet{ - def th(title1: String, titles: String*) = Calculator(Form.th(title1, titles*)) +case class Calculator(form: Form = Form()): + def tr(a: Int, b: Int, a_plus_b: Int, a_minus_b: Int) = Calculator { + def plus = prop(a + b)(a_plus_b) + def minus = prop(a - b)(a_minus_b) + form.tr(a, b, plus, minus) + } - }} +def th(title1: String, titles: String*): Calculator = + Calculator(Form.th(title1, titles*)) +}} The `Calculator` object defines a `th` method to create the first `Calculator` Form, with the proper title. The `th` method: @@ -543,13 +526,12 @@ The `Calculator` case class embeds a Form and defines a `tr` method which * returns a new Calculator containing this form $p -And you use the `Calculator` Form like this: ${snippet { - - class CalculatorSpecification extends Specification with Forms: - def is = s2""" - A calculator must add and subtract Ints ${Calculator.th("a", "b", "a + b", "a - b").tr(1, 2, 3, -1).tr(2, 2, 4, 0)} - """ - }} +And you use the `Calculator` Form like this: ${snippet{ +class CalculatorSpecification extends Specification with Forms: + def is = s2""" + A calculator must add and subtract Ints ${Calculator.th("a", "b", "a + b", "a - b").tr(1, 2, 3, -1).tr(2, 2, 4, 0)} + """ +}} Here is the output: diff --git a/guide/src/test/scala/org/specs2/guide/UseScalaCheck.scala b/guide/src/test/scala/org/specs2/guide/UseScalaCheck.scala index e55400e28b..b3bb913b31 100644 --- a/guide/src/test/scala/org/specs2/guide/UseScalaCheck.scala +++ b/guide/src/test/scala/org/specs2/guide/UseScalaCheck.scala @@ -23,20 +23,20 @@ object UseScalaCheck extends Specification with ScalaCheck with UserGuideVariabl A clever way of creating expectations in $specs2 is to use the [ScalaCheck](https://github.com/rickynils/scalacheck) library. -To declare ScalaCheck properties you first need to extend the `org.specs2.ScalaCheck` trait. Then you can pass functions returning any kind of `Result` (`Boolean`, `Result`, or a ScalaCheck `Prop`) to the `prop` method and use the resulting `Prop` as your example body: ${snippet { - s2"addition and multiplication are related ${prop { (a: Int) => a + a == 2 * a }}" - }} +To declare ScalaCheck properties you first need to extend the `org.specs2.ScalaCheck` trait. Then you can pass functions returning any kind of `Result` (`Boolean`, `Result`, or a ScalaCheck `Prop`) to the `prop` method and use the resulting `Prop` as your example body: ${snippet{ +s2"addition and multiplication are related ${prop { (a: Int) => a + a == 2 * a }}" +}} -The function that is checked can either return: ${snippet { +The function that is checked can either return: ${snippet{ // a Boolean - s2"addition and multiplication are related ${prop { (a: Int) => a + a == 2 * a }}" +s2"addition and multiplication are related ${prop { (a: Int) => a + a == 2 * a }}" // a Result - s2"addition and multiplication are related ${prop { (a: Int) => a + a must ===(2 * a) }}" +s2"addition and multiplication are related ${prop { (a: Int) => a + a must ===(2 * a) }}" // a Prop - s2"addition and multiplication are related ${prop { (a: Int) => (a > 0) ==> (a + a must ===(2 * a)) }}" - }} +s2"addition and multiplication are related ${prop { (a: Int) => (a > 0) ==> (a + a must ===(2 * a)) }}" +}} Note that if you pass functions using `Result`s you will get better failure messages than just using boolean expressions. @@ -45,28 +45,29 @@ But as you will see below there lots of different ways to parameterize ScalaChec ### Prop and Properties -You can also directly use the property types defined by ScalaCheck: `Prop` and `Properties` (a `Properties` object is a just a collection of named `Prop`s)${snippet { - val p1: Prop = Prop.forAll { (a: Int) => a + a == 2 * a } +You can also directly use the property types defined by ScalaCheck: `Prop` and `Properties` +(a `Properties` object is a just a collection of named `Prop`s)${snippet{ +val p1: Prop = Prop.forAll { (a: Int) => a + a == 2 * a } - s2"addition and multiplication are related $p1" +s2"addition and multiplication are related $p1" - val p2: Properties = new Properties("addition/multiplication") { - property("addition1") = Prop.forAll { (a: Int) => a + a == 2 * a } - property("addition2") = Prop.forAll { (a: Int) => a + a + a == 3 * a } - } - - s2"addition and multiplication are related $p2" - }} +val p2: Properties = new Properties("addition/multiplication") { + property("addition1") = Prop.forAll { (a: Int) => a + a == 2 * a } + property("addition2") = Prop.forAll { (a: Int) => a + a + a == 3 * a } +} -When using `Properties` only one example is created. This example will run each included property in turn and label the result with the property name if there is a failure. If, however, you want to create one example per included property you need to use the `properties` method: ${snippet { +s2"addition and multiplication are related $p2" +}} - val p2: Properties = new Properties("addition/multiplication") { - property("addition1") = Prop.forAll { (a: Int) => a + a == 2 * a } - property("addition2") = Prop.forAll { (a: Int) => a + a + a == 3 * a } - } +When using `Properties` only one example is created. This example will run each included property in turn +and label the result with the property name if there is a failure. If, however, you want to create one example per included property you need to use the `properties` method: ${snippet{ +val p2: Properties = new Properties("addition/multiplication") { + property("addition1") = Prop.forAll { (a: Int) => a + a == 2 * a } + property("addition2") = Prop.forAll { (a: Int) => a + a + a == 3 * a } +} - s2"addition and multiplication are related ${properties(p2)}" - }} +s2"addition and multiplication are related ${properties(p2)}" +}} *Note*: in a mutable specification the `properties` block of examples need to be added with `addFragments`: ``` @@ -77,94 +78,96 @@ If you don't do that there will be no examples executed at all (the beauty of si ### Arbitrary instances -ScalaCheck requires an implicit `Arbitrary[T]` instance for each parameter of type `T` used in a property. If you rather want to pick up a specific `Arbitrary[T]` for a given property argument you can modify the `prop` with to use another `Arbitrary` instance: ${snippet { - s2""" +ScalaCheck requires an implicit `Arbitrary[T]` instance for each parameter of type `T` used in a property. +If you rather want to pick up a specific `Arbitrary[T]` for a given property argument +you can modify the `prop` with to use another `Arbitrary` instance: ${snippet{ +s2""" a simple property $ex1 a more complex property $ex2 """ - def abStringGen = (Gen.oneOf("a", "b") |@| Gen.oneOf("a", "b"))(_ + _) +def abStringGen = (Gen.oneOf("a", "b") |@| Gen.oneOf("a", "b"))(_ + _) - given abStrings: Arbitrary[String] = - Arbitrary(abStringGen) +given abStrings: Arbitrary[String] = + Arbitrary(abStringGen) - def ex1 = prop((s: String) => s must (contain("a") or contain("b"))).setArbitrary(abStrings) +def ex1 = prop((s: String) => s must (contain("a") or contain("b"))).setArbitrary(abStrings) // use the setArbitrary method for the nth argument - def ex2 = prop((s1: String, s2: String) => (s1 + s2) must (contain("a") or contain("b"))) - .setArbitrary1(abStrings) - .setArbitrary2(abStrings) - }} +def ex2 = prop((s1: String, s2: String) => (s1 + s2) must (contain("a") or contain("b"))) + .setArbitrary1(abStrings) + .setArbitrary2(abStrings) +}} -It is also possible to pass a `Gen[T]` instance instead of an `Arbitrary[T]`: ${snippet { - val abStringGen = (Gen.oneOf("a", "b") |@| Gen.oneOf("a", "b"))(_ + _) +It is also possible to pass a `Gen[T]` instance instead of an `Arbitrary[T]`: ${snippet{ +val abStringGen = (Gen.oneOf("a", "b") |@| Gen.oneOf("a", "b"))(_ + _) - def ex1 = prop((s: String) => s must (contain("a") or contain("b"))).setGen(abStringGen) - }} +def ex1 = prop((s: String) => s must (contain("a") or contain("b"))).setGen(abStringGen) +}} ### With Shrink / Pretty -Specific `Shrink` and `Pretty` instances can also be specified at the property level: ${snippet { - val shrinkString: Shrink[String] = ??? +Specific `Shrink` and `Pretty` instances can also be specified at the property level: ${snippet{ +val shrinkString: Shrink[String] = ??? // set a specific shrink instance on the second parameter - prop((s1: String, s2: String) => s1.nonEmpty or s2.nonEmpty).setShrink2(shrinkString) +prop((s1: String, s2: String) => s1.nonEmpty or s2.nonEmpty).setShrink2(shrinkString) // set a specific pretty instance - prop((s: String) => s must (contain("a") or contain("b"))).setPretty((s: String) => - Pretty((prms: Pretty.Params) => if (prms.verbosity >= 1) s.toUpperCase else s) - ) +prop((s: String) => s must (contain("a") or contain("b"))).setPretty((s: String) => + Pretty((prms: Pretty.Params) => if (prms.verbosity >= 1) s.toUpperCase else s)) // or simply if you don't use the Pretty parameters - prop((s: String) => s must (contain("a") or contain("b"))).pretty((_: String).toUpperCase) - }} +prop((s: String) => s must (contain("a") or contain("b"))).pretty((_: String).toUpperCase) +}} -Note that it is also possible to _remove_ shrinking by appending `noShrink` to your property: ${snippet { - prop((s1: String, s2: String) => s1.nonEmpty or s2.nonEmpty).noShrink - }} +Note that it is also possible to _remove_ shrinking by appending `noShrink` to your property: ${snippet{ +prop((s1: String, s2: String) => s1.nonEmpty or s2.nonEmpty).noShrink +}} ### Contexts -ScalaCheck properties are sometimes used to test stateful applications rather than pure functions. For example you want to test that a function is writing files somewhere and you would like those files to be deleted after each property execution: ${snippet { +ScalaCheck properties are sometimes used to test stateful applications rather than pure functions. For example you want to test that a function is writing files somewhere and you would like those files to be deleted after each property execution: ${snippet{ // 8<--- - given Arbitrary[File] = ??? +given Arbitrary[File] = ??? // 8<--- - def createFile(f: File): Unit = ??? - def deleteTmpDir(): Unit = ??? +def createFile(f: File): Unit = ??? +def deleteTmpDir(): Unit = ??? - prop { (f: File) => - createFile(f) - f.exists - }.after(deleteTmpDir()) // before and beforeAfter can also be used there +prop { (f: File) => + createFile(f) + f.exists +}.after(deleteTmpDir()) // before and beforeAfter can also be used there - }} +}} -You can also "prepare" the property to be tested based on the generated arguments: ${snippet { +You can also "prepare" the property to be tested based on the generated arguments: ${snippet{ // 8<--- - given Arbitrary[File] = ??? +given Arbitrary[File] = ??? // 8<--- - def createFile(directory: File, f: File): Unit = ??? +def createFile(directory: File, f: File): Unit = ??? // this method will keep the arguments intact but can // have a side-effect to prepare the system - def setupDirectoryAndFile = (directory: File, file: File) => (directory, file) +def setupDirectoryAndFile = (directory: File, file: File) => (directory, file) - prop { (directory: File, f: File) => - createFile(directory, f) - f.exists - }.prepare(setupDirectoryAndFile) +prop { (directory: File, f: File) => + createFile(directory, f) + f.exists +}.prepare(setupDirectoryAndFile) - }} +}} -Note that there is a way to [model stateful systems](https://github.com/rickynils/scalacheck/wiki/User-Guide#stateful-testing) with ScalaCheck which goes beyond the simple setup/teardown testing done here. +Note that there is a way to [model stateful systems](https://github.com/rickynils/scalacheck/wiki/User-Guide#stateful-testing) +with ScalaCheck which goes beyond the simple setup/teardown testing done here. ### Test properties #### Default values -ScalaCheck test generation can be tuned with a few properties. If you want to change the default settings, you have to use implicit values: ${snippet { - given Parameters = Parameters(minTestsOk = 20) // add ".verbose" to get additional console printing - }} +ScalaCheck test generation can be tuned with a few properties. If you want to change the default settings, you have to use implicit values: ${snippet{ +given Parameters = Parameters(minTestsOk = 20) // add ".verbose" to get additional console printing +}} The parameters you can modify are: @@ -186,12 +189,12 @@ Note that `minTestsOk` in `specs2` corresponds to the `minSuccessfulTests` param #### Property level -It is also possible to specifically set the execution parameters on a given property: ${snippet { - class ScalaCheckSpec extends org.specs2.mutable.Specification with ScalaCheck: - "this is a specific property" >> prop { (a: Int, b: Int) => - (a + b) must ===((b + a)) - }.set(minTestsOk = 200, workers = 3) // use "display" instead of "set" for additional console printing - }} +It is also possible to specifically set the execution parameters on a given property: ${snippet{ +class ScalaCheckSpec extends org.specs2.mutable.Specification with ScalaCheck: + "this is a specific property" >> prop { (a: Int, b: Int) => + (a + b) must ===((b + a)) + }.set(minTestsOk = 200, workers = 3) // use "display" instead of "set" for additional console printing +}} #### Command-line @@ -215,17 +218,17 @@ By default, a successful example using a `Prop` will be reported as 1 success an ### Collect values -It is important to validate that generated values are meaningful. In order to do this you can use `collect` to collect values: ${snippet { +It is important to validate that generated values are meaningful. In order to do this you can use `collect` to collect values: ${snippet{ // for a property with just one argument - prop((i: Int) => i % 2 == 0).collect +prop((i: Int) => i % 2 == 0).collect // for a property with just 2 arguments // collect the second value only - prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collect2 +prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collect2 // collect the second value but map it to something else - prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collectArg2((n: Int) => "the value " + n) +prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collectArg2((n: Int) => "the value " + n) // collect all values and display - prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collectAll.verbose - }} +prop((i: Int, j: Int) => i > 0 && j % 2 == 0).collectAll.verbose +}} Note that, by default, nothing will be printed on screen unless you set the reporting to `verbose` by either: @@ -239,20 +242,20 @@ The `==>` operator in ScalaCheck helps you specify under which conditions a give However it only works one way, you cannot declare that a property must be true "if and only if" some conditions are respected. With $specs2 and the `org.specs2.execute.ResultImplicits` trait you can use the `<==>` operator to declare the equivalence of 2 `Results`, -whether they are properties or booleans or `MatchResults`. So you can write: ${snippet { +whether they are properties or booleans or `MatchResults`. So you can write: ${snippet{ // 8<--- - def isYoung(i: Int): Boolean = true +def isYoung(i: Int): Boolean = true // 8<--- // replace 55 with whatever you think "old" is... - prop((i: Int) => (i >= 18 && i <= 55) <==> isYoung(i)) - }} +prop((i: Int) => (i >= 18 && i <= 55) <==> isYoung(i)) +}} ### Working with Seeds By default when a property fails the seed will be displayed (unless you `setVerbosity` to a negative number). -If you want to "replay" a property with a that specific seed you can copy and set it on the property: ${snippet { - prop((i: Int) => i % 2 == 0).setSeed("f7ZhfyfeJz5eRysok6qBmtvt4SOxHjCIBNgn3Yhs5SD") - }} +If you want to "replay" a property with a that specific seed you can copy and set it on the property: ${snippet{ +prop((i: Int) => i % 2 == 0).setSeed("f7ZhfyfeJz5eRysok6qBmtvt4SOxHjCIBNgn3Yhs5SD") +}} You can also pass it on the command-line ``` diff --git a/guide/src/test/scala/org/specs2/guide/UserGuidePage.scala b/guide/src/test/scala/org/specs2/guide/UserGuidePage.scala index 4ec6a3dd08..014b5f07c3 100644 --- a/guide/src/test/scala/org/specs2/guide/UserGuidePage.scala +++ b/guide/src/test/scala/org/specs2/guide/UserGuidePage.scala @@ -7,8 +7,7 @@ import specification.* /** base class for creating specs2 user guide pages. */ -abstract class UserGuidePage extends Specification with UserGuideVariables with Snippets with Forms { +abstract class UserGuidePage extends Specification with UserGuideVariables with Snippets with Forms: override def map(fs: =>Fragments) = super.map(fs.compact) -} abstract class UserGuideCard extends Card with UserGuideVariables with Forms diff --git a/guide/src/test/scala/org/specs2/guide/matchers/ContentMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/ContentMatchers.scala index 1734eb0069..cab4c21bb0 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/ContentMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/ContentMatchers.scala @@ -11,79 +11,69 @@ object ContentMatchers extends UserGuideCard with matcher.ContentMatchers { ##### File contents -The matchers from the `org.specs2.matcher.ContentMatchers` trait can help us check the contents of files. For example we can check that 2 text files have the same lines: ${snippet { - (file1, file2) must haveSameLines - file1 must haveSameLinesAs(file2) - }} +The matchers from the `org.specs2.matcher.ContentMatchers` trait can help us check the contents of files. For example we can check that 2 text files have the same lines: ${snippet{ + (file1, file2) must haveSameLines + file1 must haveSameLinesAs(file2) +}} -We can check that the content of one file is contained in another one: ${snippet { +We can check that the content of one file is contained in another one: ${snippet{ + file1 must containLines(file2) +}} - file1 must containLines(file2) - - }} - -If the files are binary files we can also check that they have the same MD5 hash: ${snippet { - - (file1, file2) must haveSameMD5 - file1 must haveSameMD5As(file2) - - }} +If the files are binary files we can also check that they have the same MD5 hash: ${snippet{ + (file1, file2) must haveSameMD5 + file1 must haveSameMD5As(file2) +}} ***Order*** -It is possible to relax the constraint by requiring the equality or containment to be true regardless of the order of lines: ${snippet { - - (file1, file2) must haveSameLines.unordered - file1 must haveSameLinesAs(file2).unordered - file1 must containLines(file2).unordered - }} +It is possible to relax the constraint by requiring the equality or containment to be true regardless of the order of lines: ${snippet{ + (file1, file2) must haveSameLines.unordered + file1 must haveSameLinesAs(file2).unordered + file1 must containLines(file2).unordered +}} ***Show differences*** By default only the different lines are being shown with a bit of context (lines before and after the differences). -You can however change this strategy. For example if there are too many differences, you can specify that you only want the first 10: ${snippet { - - (file1, file2) must haveSameLines.showOnly(10.differences) - - }} +You can however change this strategy. For example if there are too many differences, you can specify that you only want the first 10: ${snippet{ + (file1, file2) must haveSameLines.showOnly(10.differences) +}} In the code above `10.differences` builds a `DifferenceFilter` which is merely a filtering function: `(lines: Seq[LineComparison]) => Seq[LineComparison])` keeping the first 10 differences. A `LineComparison` is the result of comparing a list of lines, either a line has been added, deleted, modified or is the same. ##### Directories contents -We can compare the contents of 2 directories. We can for example check if no files are missing and none has been added: ${snippet { - - actualDir must haveSamePathsAs(expectedDir) -// with a file filter applied to both the actual and expected directories - actualDir must haveSamePathsAs(expectedDir).withFilter((file: File) => !file.isHidden) +We can compare the contents of 2 directories. We can for example check if no files are missing and none has been added: ${snippet{ + actualDir must haveSamePathsAs(expectedDir) + // with a file filter applied to both the actual and expected directories + actualDir must haveSamePathsAs(expectedDir).withFilter((file: File) => !file.isHidden) +}} - }} - -Once we know that all files are present we can check their content: ${snippet { +Once we know that all files are present we can check their content: ${snippet{ // the default comparison expects that files are text files and that comparison must be done line by line - actualDir must haveSameFilesAs(expectedDir) +actualDir must haveSameFilesAs(expectedDir) // with a file filter applied to both the actual and expected directories - actualDir must haveSameFilesAs(expectedDir).withFilter((file: File) => !file.isHidden) +actualDir must haveSameFilesAs(expectedDir).withFilter((file: File) => !file.isHidden) // with a MD5 matcher for binary files - actualDir must haveSameFilesAs(expectedDir).withMatcher(haveSameMD5) +actualDir must haveSameFilesAs(expectedDir).withMatcher(haveSameMD5) // it is also possible to only check the content of actual files when they exist in the expected directory - actualDir must haveSameFilesContentAs(expectedDir) - - }} +actualDir must haveSameFilesContentAs(expectedDir) +}} ##### Lines contents -Files are not the only possible source of lines and it is useful to be able to check the content of a `File` with a `Seq[String]`: ${snippet { +Files are not the only possible source of lines and it is useful to be able to check the content of a `File` with a `Seq[String]`: ${snippet{ - file1 must haveSameLinesAs(Seq(line1, line2, line3)) + file1 must haveSameLinesAs(Seq(line1, line2, line3)) - }} +}} This is because those 2 types implement the `org.specs2.text.LinesContent` trait, defining: diff --git a/guide/src/test/scala/org/specs2/guide/matchers/ExceptionMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/ExceptionMatchers.scala index 2891abef51..087a28686d 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/ExceptionMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/ExceptionMatchers.scala @@ -16,7 +16,7 @@ object ExceptionMatchers extends UserGuideCard { `throwA(exception).like { case e => e must matchSomething }` checks that the thrown exception satisfies a property * `throwA[ExceptionType](me.like { case e => e must matchSomething }` or `throwA(exception).like { case e => e must matchSomething }` checks that the thrown exception satisfies a property -$p +---- For all the above matchers you can use `throwAn` instead of `throwA` if the exception name starts with a vowel for better readability. """ diff --git a/guide/src/test/scala/org/specs2/guide/matchers/FutureMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/FutureMatchers.scala index 6aaa2014f9..e77754a54e 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/FutureMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/FutureMatchers.scala @@ -18,45 +18,43 @@ instance (meaning that `R` is some kind of result like: `Boolean`, `Result`,...) Then your future will be executed when $specs2 executes your example and the result will be collected. However you will not get the possibility to specify retries or timeouts. For retries and timeouts -you can use the `await` method on matchers: ${snippet { - Future(1) must be_>(0).await - }} +you can use the `await` method on matchers: ${snippet{ +Future(1) must be_>(0).await +}} -You can specify a timeout value and a number of retries ${snippet { - Future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis) +You can specify a timeout value and a number of retries ${snippet{ +Future { Thread.sleep(100); 1 } must be_>(0).await(retries = 2, timeout = 100.millis) // only retries, timeout is 1.second - Future { Thread.sleep(100); 1 } must be_>(0).retryAwait(retries = 2) +Future { Thread.sleep(100); 1 } must be_>(0).retryAwait(retries = 2) // only timeout, retries = 0 - Future { Thread.sleep(100); 1 } must be_>(0).awaitFor(100.millis) - }} +Future { Thread.sleep(100); 1 } must be_>(0).awaitFor(100.millis) +}} Another possibility is for you to obtain a `Future[Result]` (or any `Future[R]` where `R` has an `AsResult` typeclass instance). -In that case you can use `await` directly on the `Future` to get a `Result`${snippet { - Future(1 === 1).await - Future(1 === 1).await(retries = 2, timeout = 100.millis) - }} +In that case you can use `await` directly on the `Future` to get a `Result`${snippet{ +Future(1 === 1).await +Future(1 === 1).await(retries = 2, timeout = 100.millis) +}} #### Execution -The `await` method require an implicit `org.specs2.concurrent.ExecutionEnv` (see [here](org.specs2.guide.ExecutionEnvironments.html) for more details). You can pass one in the body of your examples:${snippet { +The `await` method require an implicit `org.specs2.concurrent.ExecutionEnv` (see [here](org.specs2.guide.ExecutionEnvironments.html) for more details). You can pass one in the body of your examples:${snippet{ - class MyFutureSpec(using ee: ExecutionEnv) extends Specification: - def is = s2""" +class MyFutureSpec(using ee: ExecutionEnv) extends Specification: + def is = s2""" Let's check this scala future ${Future(1) must be_>(0).await} """ // in a mutable specification - class MyMutableFutureSpec(using ee: ExecutionEnv) extends mutable.Specification: - - "Let's check this scala future" >> { - Future(1) must be_>(0).await - } - - }} +class MyMutableFutureSpec(using ee: ExecutionEnv) extends mutable.Specification: + "Let's check this scala future" >> { + Future(1) must be_>(0).await + } +}} #### Time factor diff --git a/guide/src/test/scala/org/specs2/guide/matchers/InterpreterMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/InterpreterMatchers.scala index ea08a2f514..92e0d027b3 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/InterpreterMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/InterpreterMatchers.scala @@ -7,16 +7,15 @@ import matcher.ScalaInterpreterMatchers object InterpreterMatchers extends UserGuideCard with matcher.ScalaInterpreterMatchers { def title = "Scala Interpreter" def text = s2""" -In the rare case where you want to use the Scala interpreter and execute a script: ${snippet { +In the rare case where you want to use the Scala interpreter and execute a script: ${snippet{ - class ScalaInterpreterMatchersSpec extends org.specs2.mutable.Spec with ScalaInterpreterMatchers { - def interpret(s: String): String = "" // you have to provide your own Scala interpreter here - - "A script can be interpreted" >> { - "1 + 1" >| "2" - } - } - }} +class ScalaInterpreterMatchersSpec extends org.specs2.mutable.Spec with ScalaInterpreterMatchers { + def interpret(s: String): String = "" // you have to provide your own Scala interpreter here + "A script can be interpreted" >> { + "1 + 1" >| "2" + } +} +}} """ def interpret(s: String) = s } diff --git a/guide/src/test/scala/org/specs2/guide/matchers/JsonMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/JsonMatchers.scala index 71427e3693..f6f87ca434 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/JsonMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/JsonMatchers.scala @@ -22,7 +22,7 @@ object JsonMatchers extends UserGuideCard with matcher.JsonMatchers { * `/#(i)` selects the ith element in a 0-based indexed Array or a Map and allow further checks on that element -Now the interesting part comes from the fact that those matchers can be chained to search specific paths in the Json document. For example, for the following document: ${snippet { +Now the interesting part comes from the fact that those matchers can be chained to search specific paths in the Json document. For example, for the following document: ${snippet{ // taken from an example in the Lift project val person = """{ @@ -42,34 +42,34 @@ Now the interesting part comes from the fact that those matchers can be chained () }} -You can use these combinations: ${snippet { - person must /("person") */ ("person") / ("age" -> 33.0) // by default numbers are parsed as Doubles - }} +You can use these combinations: ${snippet{ +person must /("person") */ ("person") / ("age" -> 33.0) // by default numbers are parsed as Doubles +}} -You can as well use regular expressions or String matchers instead of values to verify the presence of keys or elements. For example: ${snippet { - person must /("p.*".r) */ ".*on".r / ("age" -> "\\d+\\.\\d".r) - person must /("p.*".r) */ ".*on".r / ("age" -> startWith("3")) - person must /("p.*".r) */ ".*on".r / ("age" -> (be_>(30) ^^ ((_: String).toInt))) - }} +You can as well use regular expressions or String matchers instead of values to verify the presence of keys or elements. For example: ${snippet{ +person must /("p.*".r) */ ".*on".r / ("age" -> "\\d+\\.\\d".r) +person must /("p.*".r) */ ".*on".r / ("age" -> startWith("3")) +person must /("p.*".r) */ ".*on".r / ("age" -> (be_>(30) ^^ ((_: String).toInt))) +}} -You can also access some records by their index: ${snippet { - person must /("person") /# 2 / "person" - }} +You can also access some records by their index: ${snippet{ +person must /("person") /# 2 / "person" +}} -Finally you can use Json matchers to match elements in an array: ${snippet { - val json = """{"products":[{"name":"shirt","price":10, "ids":["1", "2", "3"]},{"name":"shoe","price":5}]}""" +Finally you can use Json matchers to match elements in an array: ${snippet{ +val json = """{"products":[{"name":"shirt","price":10, "ids":["1", "2", "3"]},{"name":"shoe","price":5}]}""" - def aProductWith(name: Matcher[JsonType], price: Matcher[JsonType]): Matcher[String] = - /("name").andHave(name) and /("price").andHave(price) +def aProductWith(name: Matcher[JsonType], price: Matcher[JsonType]): Matcher[String] = + /("name").andHave(name) and /("price").andHave(price) - def haveProducts(products: Matcher[String]*): Matcher[String] = - /("products").andHave(allOf(products*)) +def haveProducts(products: Matcher[String]*): Matcher[String] = + /("products").andHave(allOf(products*)) - json must haveProducts( - aProductWith(name = "shirt", price = 10) and /("ids").andHave(exactly("1", "2", "3")), - aProductWith(name = "shoe", price = 5) - ) - }} +json must haveProducts( + aProductWith(name = "shirt", price = 10) and /("ids").andHave(exactly("1", "2", "3")), + aProductWith(name = "shoe", price = 5) +) +}} The `andHave` method accepts any `Matcher[JsonType]` where `JsonType` is either `JsonArray`, `JsonMap`, `JsonNumber`, `JsonString`, `JsonNull`. In the example above we pass directly `shirt` and `10` as `Matcher[JsonType]` because there are implicit conversions from `Int`, `Boolean`, `Double`, `String` and `Traversable[String]` matchers (like `allOf`) to a `Matcher[JsonType]`. diff --git a/guide/src/test/scala/org/specs2/guide/matchers/OutsideSpecs2.scala b/guide/src/test/scala/org/specs2/guide/matchers/OutsideSpecs2.scala index ab47056d4a..901d7f34e7 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/OutsideSpecs2.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/OutsideSpecs2.scala @@ -21,22 +21,22 @@ The $specs2 matchers are a well-delimited piece of functionality that you should #### Without any dependency on specs2 -It is possible to add testing features to your library without depending on a specific testing library, like $specs2 or ScalaTest. You will let clients of your library decide which one they want with the following trait:${snippet { - trait TestInterface: - def fail(msg: String): Nothing - def skip(msg: String): Nothing +It is possible to add testing features to your library without depending on a specific testing library, like $specs2 or ScalaTest. +You will let clients of your library decide which one they want with the following trait:${snippet{ +trait TestInterface: + def fail(msg: String): Nothing + def skip(msg: String): Nothing // and use the trait in your library - trait TestKit extends TestInterface: - def runTest(call: =>Unit) = - // run the code and if there is an error - fail("error!") +trait TestKit extends TestInterface: + def runTest(call: =>Unit) = + // run the code and if there is an error + fail("error!") - }} +}} -When there is a failure or an error the library will call the `TestKit` methods. Then the library client can use both the library and $specs2 by mixing in the `${fullName[ - matcher.ThrownMessages - ]}` trait: +When there is a failure or an error the library will call the `TestKit` methods. Then the library client can use both the library and $specs2 by mixing in the +`${fullName[matcher.ThrownMessages]}` trait: ``` trait ThrownMessages: this: ThrownExpectations => diff --git a/guide/src/test/scala/org/specs2/guide/matchers/ResultMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/ResultMatchers.scala index db98ee63c6..5abc82bdd1 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/ResultMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/ResultMatchers.scala @@ -5,14 +5,14 @@ package matchers object ResultMatchers extends UserGuideCard { def title = "Result" def text = s2""" -That's only if you want to check the result of other matchers! ${snippet { +That's only if you want to check the result of other matchers! ${snippet{ // you need to extend the ResultMatchers trait - class MatchersSpec extends Specification with matcher.ResultMatchers: - def is = - "beMatching is using a regexp" ! { - ("Hello" must beMatching("h.*")) must beSuccessful - } - }} +class MatchersSpec extends Specification with matcher.ResultMatchers: + def is = + "beMatching is using a regexp" ! { + ("Hello" must beMatching("h.*")) must beSuccessful + } +}} """ } diff --git a/guide/src/test/scala/org/specs2/guide/matchers/TerminationMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/TerminationMatchers.scala index 53e991d2e9..3c799f614b 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/TerminationMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/TerminationMatchers.scala @@ -9,35 +9,33 @@ object TerminationMatchers extends UserGuideCard with matcher.TerminationMatcher def title = "Termination" def text = s2""" -Sometimes you just want to specify that a block of code is going to terminate. The `${fullName[ - matcher.TerminationMatchers - ]}` trait is here to help. If you mix in that trait, you can write: ${snippet { +Sometimes you just want to specify that a block of code is going to terminate. +The `${fullName[matcher.TerminationMatchers]}` trait is here to help. If you mix in that trait, you can write: ${snippet{ // 8<---- - given ExecutionEnv = ??? // import your own +given ExecutionEnv = ??? // import your own // 8<---- - Thread.sleep(100) must terminate +Thread.sleep(100) must terminate // the default is retries = 0, sleep = 100.millis - Thread.sleep(100) must terminate(retries = 1, sleep = 60.millis) - - }} +Thread.sleep(100) must terminate(retries = 1, sleep = 60.millis) +}} Note that the behaviour of this matcher is a bit different from the `eventually` operator. In this case, we let the current Thread sleep during the given `sleep` time and then we check if the computation is finished, then, we retry for the given number of `retries`. -In a further scenario, we might want to check that triggering another action is able to unblock the first one: ${snippet { +In a further scenario, we might want to check that triggering another action is able to unblock the first one: ${snippet{ // 8<---- - given ExecutionEnv = ??? // import your own +given ExecutionEnv = ??? // import your own // 8<---- - action must terminate.when(action2) - action must terminate.when("starting the second action", action2) - action must terminate(retries = 3, sleep = 100.millis).when(action2) - }} +action must terminate.when(action2) +action must terminate.when("starting the second action", action2) +action must terminate(retries = 3, sleep = 100.millis).when(action2) +}} -When a second action is specified like that, `Action` will be started and `action2` will be started on the first retry. Otherwise, if you want to specify that `Action` can *only* terminate when `action2` is started, you write: ${snippet { +When a second action is specified like that, `Action` will be started and `action2` will be started on the first retry. Otherwise, if you want to specify that `Action` can *only* terminate when `action2` is started, you write: ${snippet{ // 8<---- - given ExecutionEnv = ??? // import your own +given ExecutionEnv = ??? // import your own // 8<---- - action must terminate.onlyWhen(action2) +action must terminate.onlyWhen(action2) }} #### ExecutionEnv diff --git a/guide/src/test/scala/org/specs2/guide/matchers/TraversableMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/TraversableMatchers.scala index 3481bfbaf7..92fb2f850e 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/TraversableMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/TraversableMatchers.scala @@ -8,85 +8,85 @@ object TraversableMatchers extends UserGuideCard { Traversables can be checked with several matchers. If you want to check the size of a `Traversable` * check if it is empty - ${snippet { Seq() must beEmpty }} - ${snippet { Seq(1, 2, 3) must not(beEmpty) }} + ${snippet{ Seq() must beEmpty }} + ${snippet{ Seq(1, 2, 3) must not(beEmpty) }} * check its size - ${snippet { Seq(1, 2) must haveSize(2) }} - ${snippet { Seq(1, 2) must haveLength(2) }} // equivalent to size - ${snippet { Seq(1, 2) must haveSize(be_>=(1)) }} // with a matcher + ${snippet{ Seq(1, 2) must haveSize(2) }} + ${snippet{ Seq(1, 2) must haveLength(2) }} // equivalent to size + ${snippet{ Seq(1, 2) must haveSize(be_>=(1)) }} // with a matcher * check its ordering (works with any type `T` which has an `Ordering`) - ${snippet { Seq(1, 2, 3) must beSorted }} + ${snippet{ Seq(1, 2, 3) must beSorted }} #### Check each element individually Then you can check the elements which are contained in the Traversable * if a simple value is contained - ${snippet { Seq(1, 2, 3) must contain(2) }} + ${snippet{ Seq(1, 2, 3) must contain(2) }} * if a value matching a specific matcher is contained - ${snippet { Seq(1, 2, 3) must contain(be_>=(2)) }} + ${snippet{ Seq(1, 2, 3) must contain(be_>=(2)) }} * if a value passing a function returning a `Result` is contained - ${snippet { Seq(1, 2, 3) must contain((i: Int) => i must be_>=(2)) }} + ${snippet{ Seq(1, 2, 3) must contain((i: Int) => i must be_>=(2)) }} * note that a `Seq[A]` is also a function `Int => A` so if you want to check that a sequence is contained in another you need to use a matcher - ${snippet { Seq(Seq(1)) must contain(===(Seq(1))) }} + ${snippet{ Seq(Seq(1)) must contain(===(Seq(1))) }} * there are also 2 specialized matchers to check the string representation of the elements - ${snippet { Seq(1234, 6237) must containMatch("23") }} `// matches with ".*23.*"` - ${snippet { Seq(1234, 6234) must containPattern(".*234") }} `// matches with ".*234"` + ${snippet{ Seq(1234, 6237) must containMatch("23") }} `// matches with ".*23.*"` + ${snippet{ Seq(1234, 6234) must containPattern(".*234") }} `// matches with ".*234"` For each of the check above you can indicate how many times the check should be satisfied: - * ${snippet { Seq(1, 2, 3) must contain(be_>(0)).forall }} // this will stop after the first failure - * ${snippet { Seq(1, 2, 3) must contain(be_>(0)).foreach }} // this will report all failures - * ${snippet { Seq(1, 2, 3) must contain(be_>(0)).atLeastOnce }} - * ${snippet { Seq(1, 2, 3) must contain(be_>(2)).atMostOnce }} - * ${snippet { Seq(1, 2, 3) must contain(be_>(2)).exactly(1.times) }} - * ${snippet { Seq(1, 2, 3) must contain(be_>(2)).exactly(1) }} - * ${snippet { Seq(1, 2, 3) must contain(be_>(1)).between(1.times, 2.times) }} - * ${snippet { Seq(1, 2, 3) must contain(be_>(1)).between(1, 2) }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(0)).forall }} // this will stop after the first failure + * ${snippet{ Seq(1, 2, 3) must contain(be_>(0)).foreach }} // this will report all failures + * ${snippet{ Seq(1, 2, 3) must contain(be_>(0)).atLeastOnce }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(2)).atMostOnce }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(2)).exactly(1.times) }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(2)).exactly(1) }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(1)).between(1.times, 2.times) }} + * ${snippet{ Seq(1, 2, 3) must contain(be_>(1)).between(1, 2) }} #### Check all elements The other types of checks involve comparing the Traversable elements to other elements (values, matchers, function returning a `Result`) * with a set of values - ${snippet { Seq(1, 2, 3, 4) must contain(2, 4) }} + ${snippet{ Seq(1, 2, 3, 4) must contain(2, 4) }} which is the same thing as - ${snippet { Seq(1, 2, 3, 4) must contain(allOf(2, 4)) }} + ${snippet{ Seq(1, 2, 3, 4) must contain(allOf(2, 4)) }} * with a set of matchers - ${snippet { Seq(1, 2, 3, 4) must contain(allOf(be_>(0), be_>(1))) }} + ${snippet{ Seq(1, 2, 3, 4) must contain(allOf(be_>(0), be_>(1))) }} * checking that the order is satisfied - ${snippet { Seq(1, 2, 3, 4) must contain(allOf(be_>(0), be_>(1)).inOrder) }} + ${snippet{ Seq(1, 2, 3, 4) must contain(allOf(be_>(0), be_>(1)).inOrder) }} Note that `allOf` tries to make each check be successful at least once, even if it is on the same value. On the other hand, if you want to specify that each check must succeed on a *different* value you should use `onDistinctValues`. For example this will fail: -${snippet { Seq(1) must contain(allOf(1, 1)).onDistinctValues }} +${snippet{ Seq(1) must contain(allOf(1, 1)).onDistinctValues }} The `eachOf` method does the same thing (and this example will fail as well): -${snippet { Seq(1) must contain(eachOf(1, 1)) }} +${snippet{ Seq(1) must contain(eachOf(1, 1)) }} Another frequent use of Traversable matchers is to check if the Traversable have the right number of elements. For this you can use: * `atLeast`, which is actually another name for `allOf`, where the traversable can contain more elements than required - ${snippet { Seq(1, 2, 3, 4) must contain(atLeast(2, 4)) }} + ${snippet{ Seq(1, 2, 3, 4) must contain(atLeast(2, 4)) }} * `atMost` where the traversable can not contain more elements than required - ${snippet { Seq(2, 3) must contain(atMost(2, 3, 4)) }} + ${snippet{ Seq(2, 3) must contain(atMost(2, 3, 4)) }} * `exactly` where the traversable must contain exactly the specified number of elements - ${snippet { Seq(1, 2) must contain(exactly(2, 1)) }} + ${snippet{ Seq(1, 2) must contain(exactly(2, 1)) }} The `atLeast/atMost/exactly` operators work on distinct values by default (because this is easier for counting the correspondence between actual values and expected ones). However you can use `onDistinctValues(false)` if you don't care. Finally, if you want to get the differences between 2 traversables: - ${snippet { Seq(2, 4, 1) must containTheSameElementsAs(Seq(1, 4, 2)) }} + ${snippet{ Seq(2, 4, 1) must containTheSameElementsAs(Seq(1, 4, 2)) }} """ diff --git a/guide/src/test/scala/org/specs2/guide/matchers/TypecheckMatchers.scala b/guide/src/test/scala/org/specs2/guide/matchers/TypecheckMatchers.scala index a3da47891d..454f4217fa 100644 --- a/guide/src/test/scala/org/specs2/guide/matchers/TypecheckMatchers.scala +++ b/guide/src/test/scala/org/specs2/guide/matchers/TypecheckMatchers.scala @@ -14,53 +14,54 @@ object TypecheckMatchers extends UserGuideCard: ### Typecheck matchers -Some behaviours can be encoded with the type system and you just want to check that a given expression will typecheck: ${snippet { - import org.specs2.execute.*, Typecheck.* - import org.specs2.matcher.TypecheckMatchers.* - typecheck { - """ +Some behaviours can be encoded with the type system and you just want to check that a given expression will typecheck: ${snippet{ +import org.specs2.execute.*, Typecheck.* +import org.specs2.matcher.TypecheckMatchers.* + +typecheck { + """ // there must be a Monoid instance for Text Monoid[Text].zero """ - } must succeed - }} +} must succeed +}} -You might also want to check that another expression will fail to typecheck: ${snippet { - typecheck { - """ +You might also want to check that another expression will fail to typecheck: ${snippet{ +typecheck { + """ // there must be not a Monoid instance for Plane Monoid[Plane].zero """ - } must not(succeed) +} must not(succeed) - typecheck { - """ +typecheck { + """ // there must be not a Monoid instance for Plane Monoid[Plane].zero """ - } must failWith("no implicit argument of type org.specs2.fp.Monoid\\[org.specs2.guide.matchers.Plane\\] was found") - }} +} must failWith("no implicit argument of type org.specs2.fp.Monoid\\[org.specs2.guide.matchers.Plane\\] was found") +}} #### TypecheckResult -Note that you actually don't have to use the `succeed` matcher because `typecheck` returns a `TypecheckResult` object which has an `AsResult` instance: ${snippet { - "this should typecheck ok" ! typecheck { - """ - // there must be not a Monoid instance for Plane - Monoid[Plane].zero - """ - } - }} - -This is also why you can indicate that a block of code must be marked as `Pending` until it typechecks: ${snippet { - typecheck { - """ - // there must be not a Monoid instance for Plane - Monoid[Plane].zero - """ - }.pendingUntilFixed("find a way to make this typecheck!") - }} +Note that you actually don't have to use the `succeed` matcher because `typecheck` returns a `TypecheckResult` object which has an `AsResult` instance: ${snippet{ +"this should typecheck ok" ! typecheck { + """ + // there must be not a Monoid instance for Plane + Monoid[Plane].zero + """ +} +}} + +This is also why you can indicate that a block of code must be marked as `Pending` until it typechecks: ${snippet{ +typecheck { + """ + // there must be not a Monoid instance for Plane + Monoid[Plane].zero + """ + }.pendingUntilFixed("find a way to make this typecheck!") +}} """