Structure

Structure

Presentation

In this section you will learn how to:

  • create examples and expectations
  • define contexts to execute actions before/after examples
  • structure a group of specifications by using links
  • specify the execution strategy
  • work with Unit specifications

Declare examples

Styles

The Quick Start guide describes 2 styles of specifications, the unit style and the acceptance style. Both styles actually build a specification as a list of fragments.

Acceptance specification

In an acceptance specification you build a list of Fragments which are interpolated from a s2 string:

def is =                     s2"""

  this is my specification
    and example 1            $e1
    and example 2            $e2
                             """

def e1 = success
def e2 = success

This specification builds 1 piece of Text and 2 Examples which are Fragment objects. Another way to define an Example, outside the interpolated specification would be to write:

"Example description" ! { /* example body */ ok }

Please read the Fragments API page you want to know more about the low-level operators to create and chain Specification Fragments.

Unit specification

A unit specification uses should/in blocks which build Fragments by adding them to a mutable protected variable:

"The 'Hello world' string" should {
  "contain 11 characters" in {
    "Hello world" must have size(11)
  }
  "start with 'Hello'" in {
    "Hello world" must startWith("Hello")
  }
  "end with 'world'" in {
    "Hello world" must endWith("world")
  }
}

In that specification the following methods are used:

  • in to create an Example containing a Result
  • should to create a group of Examples, where should is appended to the preceding Text fragment

It is completely equivalent to writing this in an org.specs2.Specification:

def is = s2"""

 The 'Hello world' string should
   contain 11 characters ${
     "Hello world" must have size(11)
   }
   start with 'Hello' ${
     "Hello world" must startWith("Hello")
   }
   end with 'world' ${
     "Hello world" must endWith("world")
   }
"""

The Unit specifications section shows all the methods which can be used to build unit specifications fragments.

Results

An Example is a piece of text followed by anything which can be converted to an org.specs2.execute.Result (via the org.specs2.execute.AsResult typeclass):

  • a standard result (success, failure, pending,...)
  • a Matcher result
  • a boolean value
  • a ScalaCheck property
Standard

The simplest Result values are provided by the StandardResults trait (mixed-in with Specification), and match the 5
types of results provided by specs2:

  • success: the example is ok
  • failure: there is a non-met expectation
  • anError: a unexpected exception occurred
  • skipped: the example is skipped possibly at runtime because some conditions are not met
  • pending: usually means "not implemented yet"

Two additional results are also available to track the progress of features:

  • done: a Success with the message "DONE"
  • todo: a Pending with the message "TODO"
Matchers

Usually the body of an example is made of expectations using matchers:

def e1 = 1 must_== 1

You can refer to the Matchers guide to learn all about matchers and how to create expectations.

Expectations

Functional

The default Specification trait in specs2 is functional: 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":

"my example on strings" ! e1           // will never fail!

def e1 = {
  "hello" must have size(10000)        // because this expectation will not be returned,...
  "hello" must startWith("hell")
}

So the correct way of writing the example is:

"my example on strings" ! e1           // will fail

def e1 = "hello" must have size(10000) and
                           startWith("hell")
Thrown

The above functionality encourages a specification style where every expectation is carefully specified and is considered good practice by some. However you might see it as an annoying restriction. You can avoid it by mixing-in the org.specs2.matcher.ThrownExpectations trait. With that trait, any failing expectation will throw a FailureException and the rest of the example will not be executed.

There is also an additional method failure(message) to throw a FailureException at will.

Note that the ThrownExpectations traits is mixed in the mutable.Specification trait used for unit specifications and, if you wish, you revert back to not throwing exceptions on failed expectations by mixing-in the org.specs2.matcher.NoThrownExpectations trait.

All

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 one. 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:

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 so that the Specification becomes isolated unless it is already isolated or sequential.

Short-circuit

Ultimately, you may want to stop the execution of an example if one expectation is not verified. This is possible with orThrow:

  "In this example all the expectations are evaluated" >> {
    1 === 1            // this is ok
    (1 === 3).orThrow  // this fails but is never executed
    1 === 4
  }

Alternatively, orSkip will skip the rest of the example in case of a failure.

Auto-Examples

If your specification is about showing the use of a DSL or of an API, you can elide a description for the Example. This functionality is used in specs2 to specify matchers:

s2"""
beNone checks if an element is None
${ None must beNone }
${ Some(1) must not be none }
"""

In that case, the text of the example will be extracted from the source file and the output will be:

beNone checks if an element is None
+ None must beNone
+ Some(1) must not be none

G / W /T

The Given/When/Then style for writing specifications is described here.

DataTables

DataTables are generally used to pack lots of expectations inside one example. A DataTable which is used as a Result in the body of an Example will only be displayed when failing. If, on the other hand you want to display the table even when successful, to document your examples, you can omit the example description and inline the DataTable directly in the specification:

class DataTableSpec extends Specification with matcher.DataTables { def is =

  "adding integers should just work in scala"  ^ {
    "a"   | "b" | "c" |
     2    !  2  !  4  |
     1    !  1  !  2  |>
     { (a, b, c) =>  a + b must_== c }
  }
}

This specification will be rendered as:

adding integers should just work in scala+  a | b | c |
   2 | 2 | 4 |
   1 | 1 | 2 |

Example groups

When you create acceptance specifications, you have to find names to reference your examples, which can sometimes be a bit tedious. You can then get some support from the org.specs2.specification.Grouped trait. This trait provides group traits, named g1 to g22 to define groups of examples. Each group trait defines 22 variables named e1 to e22, to define examples bodies. The specification below shows how to use the Grouped trait:

class MySpecification extends Specification with Examples { def is =  s2"""
  first example in first group                                        ${g1.e1}
  second example in first group                                       ${g1.e2}

  first example in second group                                       ${g2.e1}
  second example in second group                                      ${g2.e2}
  third example in second group, not yet implemented                  ${g2.e3}
  """
}

trait Examples extends specification.Grouped with matcher.Matchers {
  // group of examples with no description
  new g1 {
    e1 := ok
    e2 := ok
  }
  // group of examples with a description for the group
  "second group of examples" - new g2 {
    e1 := ok
    e2 := ok
  }
}

Note that, if you use groups, you can use the example names right away, like g2.e3, without providing an implementation, the example will be marked as Pending.

Isolation

You can define additional variables in your group traits:

trait Local {
  def service: Service = new LocalService
}
"a group of examples" - new g1 with Local {
  e1 := ok
  e2 := ok
}
"another group of examples" - new g2 with Local {
  e1 := ok
  e2 := ok
}

However, the service variable will be shared by all the examples of each group, which can be potentially troublesome if that variable is mutated. If you want to provide complete isolation for each example, you should instead use the org.specs2.specification.Groups trait and call each group as a function:

class MySpecification extends Specification with Examples { def is = s2"""

  first example in first group                     ${g1().e1}
  second example in first group                    ${g1().e2}
                                                   """
}

trait Examples extends Groups with matcher.Matchers {
  trait Local {
    def service: Service = new LocalService
  }
  "a group of examples" - new g1 with Local {
    // each example will have its own instance of Service
    e1 := ok
    e2 := ok
  }
}

Tags

Tags can be used for 2 purposes: example selection and formatting.

Selection

You can tag specific examples or entire sections of a specification and execute them selectively from the command line. See HowTo on how to use tags.

Formatting

You can use the formatSection or formatTag methods to specify the formatting of Texts and Examples fragments with the following parameters:

  • flow: the fragment (Text or Example) shouldn't be reported with automatic indenting (default = false, set automatically to true when using s2 interpolated strings)
  • markdown: the fragment is using Markdown (default = true)
  • verbatim: indented text with more than 4 spaces must be rendered as a code block (default = true)

Contexts

In a specification some examples are very simple and just check that a function is behaving as expected. However other examples can be more complex and require a more elaborate set-up of data to:

  • to create inter-related domain objects
  • to put the environment (database, filesystem, external system) in the appropriate state

And there are usually 3 difficulties in doing that:

  1. Variables isolation: making sure that each example can be executed with its own data without being impacted by the undesired side-effects of other examples
  2. Before/After code: running code before or after every example without repeating that code in the body of each example
  3. Global setup/teardown code: setting some state when this could take lots of resources, so you need to do it just once before anything runs

How does a library like JUnit solves this?

  1. Variables isolation: for each test run a new class instance is created so that there are new "fresh" variables for the current test case
  2. Before/After code: there are @Before and @After annotations to declare once the code that must be executed before or after each example
  3. Global setup/teardown code: there are @BeforeClass and @AfterClass annotations dedicated to that kind of code

Now let's see how this can be achieved with specs2.

Isolation

specs2 solves this issue in 2 ways:

  • simply by relying on Scala features, by creating a new trait or a case class to open a new Scope with fresh variables
  • by cloning the specification on each example execution when the isolated argument is provided
Scope

Let's see an example of using a Scope with a mutable specification:

import org.specs2.specification.Scope

class ContextSpec extends mutable.Specification {
  "this is the first example" in new trees {
    tree.removeNodes(2, 3) must have size(2)
  }
  "this is the first example" in new trees {
    tree.removeNodes(2, 3, 4) must have size(1)
  }
}

/** the `trees` context */
trait trees extends Scope {
  val tree = new Tree(1, 2, 3, 4)
}

Each example of that specification gets a new instance of the trees trait. So it will have a brand new tree variable and even if this data is mutated by an example, other examples will be isolated from these changes.

Now you might wonder why the trees trait is extending the org.specs2.specification.Scope trait? The reason is that the body of an Example only accepts objects which are convertible to a Result. By extending Scope we can take advantage of an implicit conversion provided by the Specification trait to convert our context object to a Result.

Scopes are a way to create a "fresh" object and associated variables for each example being executed. The advantages are that:

  • those classes can be reused and extended
  • the execution behavior only relies on language constructs

However, sometimes, we wish to go for a more concise way of getting fresh variables, without having to create a specific trait to encapsulate them. That's what the isolated argument is for.

Isolated variables

The isolated argument changes the execution method so that each example is executed in a brand new instance of the Specification:

class IsolatedSpec extends mutable.Specification {
  isolated

  "Each example should be executed in isolation" >> {

    val tree = new Tree(1, 2, 3, 4)
    "the first example modifies the tree" >> {
      tree.removeNodes(2, 3) must have size(2)
    }
    "the second example gets an unmodified version of the tree" >> {
      tree.removeNodes(2, 3, 4) must have size(1)
    }
  }
}

Since there is a new Specification for each example, then all the variables accessible to the example will be seen as new.

Note: this technique will not work if the Specification is defined with a constructor having parameters because it won't be possible to create a new instance.

Case classes

The same kind of variable isolation can be achieved in acceptance specifications by using case classes:

class ContextSpec extends Specification { def is = s2"""
  this is the first example                          ${trees().e1}
  this is the second example                         ${trees().e2}
  """
}

case class trees() {
  val tree = createATreeWith4Nodes

  def e1 = tree.removeNodes(2, 3) must have size(2)
  def e2 = tree.removeNodes(2, 3, 4) must have size(1)
}

In this case we don't need to extend the Scope trait because the examples e1 and e2 already return Results.

Contexts inheritance

One very cool property of using traits to define context variables is that we can use inheritance to describe more and more specific contexts:

trait LoggedIn extends Scope {
  val user = logInUser
  // do something with the user
}

trait HasAPendingOrder extends LoggedIn {
  val order = createPendingOrder
  // the user is logged in
  // now do something with the user and his order
}

Before/After

If you want to run some code before or after each example, the Before and After traits are there to help you (they both extend the Scope trait). In the following examples we'll only show the use of After because Before most of the time unnecessary:

class ContextSpec extends mutable.Specification {
  "this is the first example" in new trees {
    tree.removeNodes(2, 3) must have size(2)
  }
  "this is the first example" in new trees {
    tree.removeNodes(2, 3, 4) must have size(1)
  }
}

trait trees extends Scope {
  setupDB
  lazy val tree = getATreeWith4NodesFromTheDatabase
}

Indeed when you have setup code you can do anything you want in the body of your context trait and this will be executed before the example body. However this wouldn't work with teardown code, so let's see how to use the After trait.

In a mutable specification

You make your context trait extend the org.specs2.mutable.After trait:

class ContextSpec extends mutable.Specification {
  "this is the first example" in new trees {
    tree.removeNodes(2, 3) must have size(2)
  }
  "this is the first example" in new trees {
    tree.removeNodes(2, 3, 4) must have size(1)
  }
}

trait trees extends mutable.After {
  lazy val tree = getATreeWith4NodesFromTheDatabase
  def after = cleanupDB()
}

In this case, the clean-up code defined in the after method will be executed after each example. This is possible because the mutable.After trait extends the Scala DelayedInit trait allowing to insert code around the execution of the body of an object.

Note: the org.specs2.mutable.{ Before, After, BeforeAfter } traits only work for scala > 2.9.0 because previous Scala versions don't provide the DelayedInit trait.

In an acceptance specification

In that case you would extend the specification.After trait and use the apply method:

class ContextSpec extends Specification { def is = s2"""
  this is the first example                          ${trees().e1}
  this is the second example                         ${trees().e2}
  """
  case class trees() extends specification.After {
    lazy val tree = getATreeWith4NodesFromTheDatabase
    def after = cleanupDB()

    // this is equivalent to: def e1 = this.apply { ... }
    def e1 = this { tree.removeNodes(2, 3) must have size(2) }
    def e2 = this { tree.removeNodes(2, 3, 4) must have size(1) }
  }
}

Now we have both variable isolation and non-duplication of set-up code!

But there is more to it. The next paragraphs will show how to:

  1. execute the body of each example inside a specific context: Around
  2. set-up a context object (say a http query) and pass it to each example: Outside
  3. declare a before method for all the examples of a Specification without even having to create a context object
  4. create a new context object by combining existing ones

Around

Some examples need to be executed in a given context. For example you're testing a web application and your specification code needs to have your example executed inside an Http session.

In that case you can extend the Around trait and specify the around method:

object http extends Around {
  def around[T : AsResult](t: =>T) = openHttpSession("test") {
    AsResult(t)  // execute t inside a http session
  }
}

"this is a first example where the code executes inside a http session" ! http(e1)
"and another one"                                                       ! http(e2)

Note that the context here is an object instead of a trait or case class instance because in this specification we don't need any variable isolation. We also take the advantage that objects extending Context traits (like Before / After / Around,...) have an apply method so we can directly write http(e1) meaning http.apply(e1).

Outside

Outside is bit like Around except that you can get access to the application state that you're setting in your Context object. Let's see that with an example (with a mutable Specification for a change):

object http extends Outside[HttpReq] with Scope {
  // prepare a valid HttpRequest
  def outside: HttpReq = createRequest
}

// use the http request in each example
"this is a first example where the code executes uses a http request" in http { (request: HttpReq) =>
  success
}
"and another one" in http { (request: HttpReq) =>
  success
}
AroundOutside

We can also combine both the Around and the Outside behaviors with the AroundOutside trait:

object http extends AroundOutside[HttpReq] {
  // create a context
  def around[T : AsResult](t: =>T) = {
    createNewDatabase()
    // execute the code inside a databaseSession
    inDatabaseSession { AsResult(t) }
  }
  // prepare a valid HttpRequest
  def outside: HttpReq = createRequest
}

"this is a first example where the code executes uses a http request" ! http((request: HttpReq) => success)
"and another one"                                                     ! http((request: HttpReq) => success)

Fixture

Finally, the way to get the most control on the data that is passed to each example and how it is executed is to use a Fixture:

val evenNumbers = new Fixture[Int] {
  def apply[R : AsResult](f: Int => R) = {
    // test f with 1, 2, 3
    Seq(1, 2, 3).foldLeft(Success(): Result) { (res, i) =>
      res and AsResult(f(i))
    }
  }
}

"even numbers can be divided by 2" ! evenNumbers { i: Int => i % 2 === 0 }

The example above tests repeatedly the same code with different values (you could add before or after actions if you wanted to). As you can see, the fixture can be applied explicitly to the example body but you can use the FixtureExample trait to pass the fixture to each example:

// using the FixtureExample trait
class MySpec2 extends Specification with FixtureExample[Int] { def is = s2"""
  "even numbers can be divided by 2" ${ i: Int => i % 2 === 0 }
  """

  def fixture[R : AsResult](f: Int => R): Result = ???
}

BeforeExample

When you just need to have set-up code executed before each example and if you don't need to have variable isolation, you can simply use the org.specs2.specification.BeforeExample trait.

The org.specs2.specification.BeforeExample trait allows you to define a before method exactly like the one you define in the Before trait and apply it to all the examples of the specification:

class MySpecification extends mutable.Specification with BeforeExample {
  def before = cleanDatabase()

  "This is a specification where the database is cleaned up before each example" >> {
    "first example" in { success }
    "second example" in { success }
  }
}

As you can guess, the AfterExample, AroundExample,... traits work similarly by requiring the corresponding after, around,... methods to be defined.

Composition

Combinations

specs2 contexts can be combined in several ways. When you want to define both Before and After behavior, you can do it by extending the BeforeAfter trait:

case class withFile() extends BeforeAfter {
  def before = createFile("test")
  def after  = deleteFile("test")
}

Similarly you can use BeforeAfterAround instead of Before with After with Around.

Composition

Contexts can be also be composed but only if they are of the same type, Before with Before, After with After,...

case class withFile() extends Before {
  def before = createFile("test")
}
case class withDatabase() extends Before {
  def before = openDatabase("test")
}
val init = withFile() compose withDatabase()

"Do something on the full system" ! init(success)

Console output

Some specifications might be using libraries which tend to be immoderately logging their activity. If you want to temporarily switch off any console display during the execution of the examples you can add the org.specs2.reporter.NoStdOutAroundExample trait. This trait creates an Around context for example and re-directs the output to an empty stream until the example is finished. It then resets the output to the default System.out output. Of course since System.out and Console.out are shared resources this might not work so well when specifications or examples are executed concurrently. In this case you might consider using fork in Test := true within sbt or sequential within specs2 to get a proper display.

Steps/Actions

Steps

Some set-up actions are very time-consuming and should be executed only once for the whole specification. This can be achieved by inserting some silent Steps in between fragments:

class DatabaseSpec extends Specification { def is = s2"""

This specification opens a database and execute some tests       ${ Step(openDatabase("test")) }
  example 1                                                      $success
  example 2                                                      $success
                                                                 ${ Step(closeDatabase("test")) }
  """
}

The examples are (by default) executed concurrently between the 2 steps and the "result" of those steps will never be reported unless if there is a failure.

Actions

Steps are very useful because they will really be executed sequentially, before anything else, but if you need to execute some actions which are completely independent of the rest of the specification, there is an equivalent to Step adequately called Action:

class DatabaseSpec extends Specification { def is = s2"""

  This specification opens a database and execute some tests"     ${ Step(openDatabase("test")) }
    example 1                                                     $success
    add 1 to the number of specification executions"              ${ Action(db.executionsNb += 1) }
    example 2                                                     $success
                                                                  ${ Step(closeDatabase("test")) }
                                                                  """
}

Of course, Steps and Actions are not the privilege of acceptance specifications:

class DatabaseSpec extends mutable.Specification {

  textFragment("This specification opens a database and execute some tests")
  step(openDatabase("test"))

  "example 1" in success

  textFragment("add 1 to the number of specification executions")
  action(db.executionsNb += 1)

  "example 2" in success
  step(closeDatabase("test"))
}

Template

There may still be some duplication of code if you have to use the same kind of set-up procedure for several specifications.

If that's the case you can define your own Specification trait doing the job:

import org.specs2._
import specification._

trait DatabaseSpec extends Specification {
  /** the map method allows to "post-process" the fragments after their creation */
  override def map(fs: =>Fragments) = Step(startDb) ^ fs ^ Step(cleanDb)
}

The DatabaseSpec above will insert, in each inherited specification, one Step executed before all the fragments, and one executed after all of them.

Global setup/teardown

The next similar thing you might want to do is to do some setup, like create a database schema, once and for all before any specification runs. The easiest way to do that is to use an object and a lazy variable:

object databaseSetup  {
  lazy val createDb = { println("creating the database") }
}

// use the createDB lazy val to create the database once for every specification inheriting from
// the DatabaseSpec trait
trait DatabaseSpec extends Specification {
  lazy val dbSetup = databaseSetup
  override def map(fs: =>Fragments) = Step(dbSetup.createDb) ^ Step(startDb) ^ fs ^ Step(cleanDb)
}

Note also that global setup and cleanup can be done with sbt.

For fragments

When using a Unit Specification, it can be useful to use variables which are only used for a given set of examples. This can be easily done by declaring local variables, but this might lead to duplication. One way to avoid that is to use the org.specs2.mutable.NameSpace trait:

trait context extends mutable.NameSpace {
  var variable1 = 1
  var variable2 = 2
}

"this is the first block" >> new context {
  "using one variable"      >> { variable1 === 1 }
  "using a second variable" >> { variable2 === 2 }
}
"this is the second block" >> new context {
  "using one variable"      >> { variable1 === 1 }
  "using a second variable" >> { variable2 === 2 }
}

Links

There are 2 ways to "link" specifications:

  • by including another specification, to create a parent-child relationship
  • by creating a reference to another specification, to create a peer relationship

Inclusion

There is a simple mechanism for including a "children" specification in a given specification. You can simply add the child specification as if it was a simple fragment:

s2"""
This is an included specification
  $childSpec
"""

Otherwise, if you want to include several specifications at once you can use the include method:

s2"""
These are the included specifications
  ${include(childSpec1, childSpec2, childSpec3)}
"""

The effect of doing so is that all the fragments of the children specification will be inlined in the parent one. This is exactly what is done in some pages of this user guide, but with a twist:

s2"""
  ${include(xonly, new HelloWorldSpec) }
  ${include(xonly, new HelloWorldUnitSpec)}
"""

In the code above there are specific arguments to the included specifications so that they are only displayed when there are failures.

Inline

When you include a specification in another one the console will display the beginning and end statistics of the included specification. If you just want to insert the "middle" fragments of the included specification you can use inline:

s2"""
  ${inline(otherSpecification)}
"""
Html link

In order to create a User Guide such as this one, you might want the included specification to be written to another html file. In this case, you need a "Link":

s2"""
  ${link(new QuickStart)}
"""

This declaration will include the child specification so it is executed when the parent specification is executed. However during the reporting, only a Html link will be created in the parent file, referencing a separate file for the children specification. On the other hand if you "hide" the specification, the link will not be printed out:

s2"""
  ${link((new QuickStart).hide)}
 """
Html Link

It is possible to customize the generated Html link with the following syntax:

${"a " ~ (new QuickStart, "for you")}

The ~ operator is used to create a HtmlLink where:

  • "a" is the beginning of the text
  • the QuickStart specification title is the text that will be highlighted as a html link
  • new QuickStart is the specification to include, the url being derived from the specification class name (or user-defined if you defined the specification title with an additional urlIs)

Several variations are possible on this pattern, depending which part of the link you want to be highlighted:

  ${"before text" ~ (specification, "after text")                                      }
  ${"before text" ~ ("other text to highlight", specification, "after text", "tooltip")}
  ${"text to highlight" ~ specification                                                }
  ${"text to highlight" ~ (specification, "after text")                                }
  ${"text to highlight" ~ (specification, "after text", "tooltip")                     }

Reference

Sometimes you just want to reference another specification without triggering its execution. For example when creating an index page:

${see(new MailSenderSpec)}

This will generate a html link in the main specification based on the referenced specification name. If you want to customize that link you can use the following syntax:

  ${"before text" ~/ ("text to highlight", specification, "after text")           }
  ${"before text" ~/ ("text to highlight", specification, "after text", "tooltip")}
  ${"text to highlight" ~/ specification                                          }
  ${"text to highlight" ~/ (specification, "after text")                          }
  ${"text to highlight" ~/ (specification, "after text", "tooltip")               }

Markdown url

If you just want to reference the url of the html page that's being generated for a given specification in a paragraph of text, you can use the markdownLink method:

s"""
  For more information you can read ${DetailedSpec.markdownLink}
  // or
  For more information you can read ${DetailedSpec.markdownLink("the detailed specification")}
  // or
  For more information you can read ${"the detailed specification".markdownLink(DetailedSpec)}
"""

Execution

This section summarizes the execution algorithm of a specification based on its fragments:

  1. all the fragments are divided into groups delimited by Steps
  2. if the sequential argument is present, each fragment goes to its own group
  3. groups are executed sequentially and all the fragments of a given group are executed concurrently
  4. if the isolated argument is present, each example is executed in its own version of the Specification
  5. if the isolated argument is present, all the Steps preceding an example are executed before that example
  6. if the Specification inherits from the AllExpectations trait, then it is executed as an isolated Specification unless it is already set as sequential
  7. if the stopOnFail argument is present, all the examples in the next group of fragments will be skipped if there is a failure in one of the previous groups
  8. if the stopOnSkip argument is present, all the examples in the next group of fragments will be skipped if there is a skipped in one of the previous groups
  9. if there is a Step created with Step.stopOnFail or Step.stopOnFail(when = true), all the examples in the next group of fragments will be skipped if there is a failure in the group before the Step

Unit specifications

Methods

Those are all the methods which you can use to create fragments in a unit specification:

  • can: create a group of Examples, with the preceding Text fragment appended with can
"a configuration" can {
  "have a name" in ok
}
  • >>: create an Example or a group of Examples (with no appended text)
"a configuration may" >> {
  "have a name" in ok
}

Note that you can use a for loop to create examples with the examplesBlock method:

"this system has 5 examples" >> {
  examplesBlock { (1 to 5) foreach { i => "example "+i >> ok } }
}

And you can also use a for loop with the Result.unit method to create a block of expectations:

"this example has 5 expectations" in {
  Result.unit { (1 to 5) foreach { i => i must_== i } }
}
  • title: give a title to the Specification
"My spec title".title
// an url path can be used to specify a different path for the html reporting
"My spec title".title.urlIs("com/MySpec.html")
  • args: create arguments for the specification

  • .txt or textFragment: create a Text fragment

"this is a text fragment".txt

textFragment("this is a text fragment")
  • step: create a Step
step { initializeDatabase() }
  • action: create an Action
action { justDoIt() }
  • link: create a link to another specification
link("how" ~ ("to do hello world", new HelloWorldSpec))
  • see: add a link to another specification without including its fragments for execution
see(new HelloWorldSpec)
  • include to include another specification
include(new HelloWorldSpec)
  • p, br, t, bt, end, endp: add a formatting fragment

Full example

To make things more concrete here is a full example:

import mutable._
import specification._
import execute.Success

/**
 * This specification shows how to use the mutable.Specification trait to create a unit Specification
 * where the fragments are built using a mutable variable
 */
class MutableSpec extends Specification {

  // A title can be added at the beginning of the specification
  "MutableSpec".title
  // arguments are simply declared at the beginning of the specification if needed
  args(xonly=true)

  "This is a unit specification showing the use of different methods".txt

  // a step to execute before the specification must be declared first
  step {
    // setup your data or initialize your database here
    success
  }

  "'Hello world'" should {
    "contain 11 characters" in {
      "Hello world" must have size(11)
    }
    "start with 'Hello'" in {
      "Hello world" must startWith("Hello")
    }
    /**
     * a failing example will stop right away, without having to "chain" expectations
     */
    "with 'world'" in {
      // Expectations are throwing exception by default so uncommenting this line will
      // stop the execution right away with a Failure
      // "Hello world" must startWith("Hi")

      "Hello world" must endWith("world")
    }
  }
  /**
   * "Context management" is handled through the use of traits or case classes
   */
  "'Hey you'" should {
    // this one uses a "before" method
    "contain 7 characters" in context {
      "Hey you" must have size(7)
    }
    // System is a Success result. If the expectations fail when building the object, the example will fail
    "contain 7 characters" in new system {
      string must have size(7)
    }
    // otherwise a case class can be used but the example body will be further down the file
    "contain 7 characters" in system2().e1
  }
  // you can add links to other specifications with `link`
  // they will be executed when this one is executed. If you don't want this to happen
  // you can use `see` instead of `link`
  link("how" ~ ("to do hello world", new HelloWorldSpec))
  // you can include other specifications with `include`
  include(new HelloWorldSpec)

  // a step to execute after the specification must be declared at the end
  step {
    // close the database here
    success
  }


  object context extends org.specs2.mutable.Before {
    def before = () // do something to setup the context
  }
  // we need to extend Scope to be used as an Example body
  trait system extends Scope {
    val string = "Hey you"
  }
  case class system2() {
    val string = "Hey you"
    def e1 = string must have size(7)
  }
}