1
votes

I just started using Scala and Spec2. However, I'm a little bit confusing of the acceptance style in spec2.

Could somebody explain it to me what's the function of ! and ^ in this case. I have read at the documentation and I still don't quite understand. I understand the unit testing pattern, but this part is really hard to understand for me.


  "this is my specification"                          ^
    "and example 1"                                   ! e1^
    "and example 2"                                   ! e2

  def e1 = success
  def e2 = success

This example I got it from http://etorreborre.github.com/specs2/guide/org.specs2.guide.Structure.html

Thank you very much.

2

2 Answers

4
votes

First, ! and ^ have been chosen with two criteria in mind:

  • Reduce visual clutter
  • Have relative precedence necessary to avoid parenthesis

What they do is: ! associates a description with a code that tests that description, and ^ concatenates a bunch of descriptions, tests, formatting annotations, and other supporting code into an object that describes the full test suite.

The point of "acceptance" specifications is they they are purely function. The mutable specifications take advantage of mutability to "register" code. For example, when you do this:

"a test" should {
  "succeed" in {
    success
  }
}

That isn't associated with any declaration. It's just a statement in the body of class (the constructor), so how does Specs2 find out about it? Simple: when that code runs, it changes the value or a variable to register itself.

That's something that immutable specs do not do. The test is found by running the method is, and that method has to return a list of all tests. Let's imagine a very OO, non syntactic sugar, way of doing that:

def is = List(
  new TestCase("A Test", new TestRule("succeed", success)),
  new TestCase("Another test", new TestRule("not fail", success))
)

So, what the method ^ does is joining "test cases" (so to speak -- not actual Specs2 terminology), and what ! does is associating a "test rule" to a "test case". It works a bit like below -- not exactly, since I'm changing stuff to make it look more like traditional OO.

"this is my specification"                        ^
"and example 1"                                   ! e1^
"and example 2"                                   ! e2

new ExampleDescription("this is my specification") ^
new ExampleDescription("and example 1")            ! e1^
new ExampleDescription("and example 2")            ! e2

// an "Example" is a subclass of "Fragment"
new ExampleDescription("this is my specification")       ^
new Example(new ExampleDescription("and example 1"), e1) ^
new Example(new ExampleDescription("and example 2"), e2)

new Fragment(
  new ExampleDescription("this is my specification"),
  List(
    new Example(new ExampleDescription("and example 1"), e1),
    new Example(new ExampleDescription("and example 2"), e2)
  )
)

new Fragments(
  new Fragment(
    new ExampleDescription("this is my specification"),
    List(
      new Example(new ExampleDescription("and example 1"), e1),
      new Example(new ExampleDescription("and example 2"), e2)
    )
  )
)

So, an acceptance specification is formed by fragments. A fragment can be composed in many ways, such as a description associated with some code, or a description followed by other fragments.

The way it works is really complex, to allow for diverse needs such as auto-examples, given-when-then specifications, regex extractors, partial functions, formatting of specification output, etc.

3
votes

It is quite simple. As written on the page ^ builds a list of fragments. Fragments can be either strings, descriptions with code blocks attached ("description" ! block) or control elements like p for a blank line. So your example can be read as:

Description("this is my specification") and
  Description("and example 1").withBlock(e1) and
  Description("and example 2").withBlock(e2)

This will be assigned as the body of the def is = at the top of your spec class. Internally this list will then be processed and for each description there will be a line printed to the console and depending on the result of the block (if it has one) it will be colored differently.

So to sum it up, it build a list of items that will be processed by the test runner.