Structure

Styles

In a Specification you generally want to include 2 things:

With specs2 you have 2 main ways to do this:

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:

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

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 with an AsExecution instance).

When the Specification is executed, the s2 string is analysed and 2 Examples are created then executed:

All the rest, "this is my specification", is parsed as Text and is not executed.

Unit specification

A unit specification extends org.specs2.mutable.Specification and uses the >> operator to create “blocks” containing Texts and Examples:

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:

However once a specification is created with all its Texts and Examples, the execution will be the same, whether it is an Acceptance one or a Unit one.

The >> blocks can be nested and this allows you to structure your specification so that the outermost blocks describe a general context while the innermost ones describe more specific contexts. A similar effect can be achieved by simply indenting text in an acceptance specification.

Expectations

There is another major difference between the acceptance specifications and unit specifications. The first style encourages you to write one expectation per example while the second allows to use several. One expectation per example is useful because when a specification fails, you know immediately what is wrong. However it is sometimes expensive to setup data for an example. In that case, having several expectations sharing the same setup might be preferable.

The good news is that for each of the 2 main styles, acceptance and unit, you can choose exactly which “Expectation mode” you prefer if the default mode is not convenient.

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:

// this will never fail!
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:

s2"""
  my example on strings $e1
"""
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:

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.

Now learn how to...

  • use matchers to specify the body of your examples
  • set up contexts for the examples
  • control the execution of a specification
  • run a specification

And if you want to know more