In this section you will learn how to:
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.
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 }
In a s2
string the description of an example is taken as all the text having the same indentation before the example body:
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
"""
If you want the example description to be unevenly aligned you can use a margin |
:
s2"""
This is the introduction paragraph
Which presents the examples
|this example has a very
| very very
| specific indentation $ok
"""
Please read the Fragments API page you want to know more about the low-level operators to create and chain Specification Fragments.
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 fragmentIt 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.
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):
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 okfailure
: there is a non-met expectationanError
: a unexpected exception occurredskipped
: the example is skipped possibly at runtime because some conditions are not metpending
: 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"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.
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")
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.
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
.
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.
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
The Given/When/Then style for writing specifications is described here.
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 |
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
.
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 can be used for 2 purposes: example selection and formatting.
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.
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
)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:
And there are usually 3 difficulties in doing that:
How does a library like JUnit solves this?
@Before
and @After
annotations to declare once the code that must be executed before or after each example@BeforeClass
and @AfterClass
annotations dedicated to that kind of codeNow let's see how this can be achieved with specs2.
specs2 solves this issue in 2 ways:
Scope
with fresh variablesisolated
argument is providedLet'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:
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.
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.
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 Result
s.
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
}
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.
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 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:
Around
Outside
before
method for all the examples of a Specification without even having to create a context objectSome 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
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
}
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)
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 = ???
}
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.
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
.
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)
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.
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 Step
s 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.
Step
s 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, Step
s and Action
s 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"))
}
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.
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.
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 }
}
There are 2 ways to "link" specifications:
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.
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)}
"""
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)}
"""
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:
QuickStart
specification title is the text that will be highlighted as a html linknew 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") }
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") }
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)}
"""
This section summarizes the execution algorithm of a specification based on its fragments:
Steps
sequential
argument is present, each fragment goes to its own groupisolated
argument is present, each example is executed in its own version of the Specificationisolated
argument is present, all the Steps
preceding an example are executed before that exampleAllExpectations
trait, then it is executed as an isolated
Specification unless it is already set as sequential
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 groupsstopOnSkip
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 groupsStep
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
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 specificationlink("how" ~ ("to do hello world", new HelloWorldSpec))
see
: add a link to another specification without including its fragments for executionsee(new HelloWorldSpec)
include
to include another specificationinclude(new HelloWorldSpec)
p, br, t, bt, end, endp
: add a formatting fragmentTo 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)
}
}