Getting started with FeatureSpec

By learning to use FeatureSpec, GivenWhenThen, simple assertions, and the BeforeAndAfter trait, you can become productive in the acceptance testing style of ScalaTest very quickly. You can then learn and use more of ScalaTest over time.

In a FeatureSpec, you describe scenarios of features. The structure looks like this:

import org.scalatest.FeatureSpec
 
class ExampleSpec extends FeatureSpec {
 
  feature("The user can pop an element off the top of the stack") {
 
    scenario("pop is invoked on a non-empty stack") (pending)
 
    scenario("pop is invoked on an empty stack") (pending)
  }
}

Each scenario in a FeatureSpec is one ScalaTest test. The scenarios are marked pending in the previous example to indicate the tests have not yet been implemented. You can compile this FeatureSpec like this:

$ scalac -cp scalatest-2.2.1.jar ExampleSpec.scala

When you run it, the feature/scenario structure shows up in the output:

$ scala -cp scalatest-2.2.1.jar org.scalatest.run ExampleSpec
Run starting. Expected test count is: 2
ExampleSpec:
Feature: The user can pop an element off the top of the stack
  Scenario: pop is invoked on a non-empty stack (pending)
  Scenario: pop is invoked on an empty stack (pending)
Run completed in 65 milliseconds.
Total number of tests run: 0
Suites: completed 1, aborted 0
Tests: succeeded 0, failed 0, ignored 0, pending 2
All tests passed.

If you wish, you can flesh out the specification by mixing in GivenWhenThen and adding extra information about the scenarios, like this:

import org.scalatest.FeatureSpec
import org.scalatest.GivenWhenThen
 
class ExampleSpec extends FeatureSpec with GivenWhenThen {
 
  feature("The user can pop an element off the top of the stack") {
 
    info("As a programmer")
    info("I want to be able to pop items off the stack")
    info("So that I can get them in last-in-first-out order")
 
    scenario("pop is invoked on a non-empty stack") {
 
      given("a non-empty stack")
      when("when pop is invoked on the stack")
      then("the most recently pushed element should be returned")
      and("the stack should have one less item than before")
      pending
    }
 
    scenario("pop is invoked on an empty stack") {
 
      given("an empty stack")
      when("when pop is invoked on the stack")
      then("NoSuchElementException should be thrown")
      and("the stack should still be empty")
      pending
    }
  }
}

When you run this version of ExampleSpec, you'll see the extra information in the report:

$ scala -cp scalatest-1.8.jar org.scalatest.run ExampleSpec
Run starting. Expected test count is: 2
ExampleSpec:
Feature: The user can pop an element off the top of the stack 
  As a programmer 
  I want to be able to pop items off the stack 
  So that I can get them in last-in-first-out order
  Scenario: pop is invoked on a non-empty stack (pending)
    Given a non-empty stack 
    When when pop is invoked on the stack 
    Then the most recently pushed element should be returned 
    And the stack should have one less item than before 
  Scenario: pop is invoked on an empty stack (pending)
    Given an empty stack 
    When when pop is invoked on the stack 
    Then NoSuchElementException should be thrown 
    And the stack should still be empty
Run completed in 112 milliseconds.
Total number of tests run: 0
Suites: completed 1, aborted 0
Tests: succeeded 0, failed 0, ignored 0, pending 2
All tests passed.

When you can't put off writing the actual tests themselves any longer, you can fill them in. Here's an example:

import org.scalatest.FeatureSpec
import org.scalatest.GivenWhenThen
import scala.collection.mutable.Stack
 
class ExampleSpec extends FeatureSpec with GivenWhenThen {
 
  feature("The user can pop an element off the top of the stack") {
 
    info("As a programmer")
    info("I want to be able to pop items off the stack")
    info("So that I can get them in last-in-first-out order")

    scenario("pop is invoked on a non-empty stack") {
 
      given("a non-empty stack")
      val stack = new Stack[Int]
      stack.push(1)
      stack.push(2)
      val oldSize = stack.size
 
      when("when pop is invoked on the stack")
      val result = stack.pop()
 
      then("the most recently pushed element should be returned")
      assert(result === 2)
 
      and("the stack should have one less item than before")
      assert(stack.size === oldSize - 1)
    }
 
    scenario("pop is invoked on an empty stack") {
 
      given("an empty stack")
      val emptyStack = new Stack[Int]
 
      when("when pop is invoked on the stack")
      then("NoSuchElementException should be thrown")
      intercept[NoSuchElementException] {
        emptyStack.pop()
      }
 
      and("the stack should still be empty")
      assert(emptyStack.isEmpty)
    }
  }
}

Now when you run ExampleSpec you'll see the tests are no longer reported as pending:

$ scala -cp scalatest-2.2.1.jar org.scalatest.run ExampleSpec
Run starting. Expected test count is: 2
ExampleSpec:
Feature: The user can pop an element off the top of the stack 
  As a programmer 
  I want to be able to pop items off the stack 
  So that I can get them in last-in-first-out order 
  Scenario: pop is invoked on a non-empty stack
    Given a non-empty stack 
    When when pop is invoked on the stack 
    Then the most recently pushed element should be returned 
    And the stack should have one less item than before 
  Scenario: pop is invoked on an empty stack
    Given an empty stack 
    When when pop is invoked on the stack 
    Then NoSuchElementException should be thrown 
    And the stack should still be empty 
Run completed in 96 milliseconds.
Total number of tests run: 2
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 0, ignored 0, pending 0
All tests passed.

Using assertions

To get started quickly with ScalaTest, learn to use assert with the === operator and intercept. Later if you prefer you can switch to ScalaTest's matchers.

ScalaTest lets you use Scala's assertion syntax, but defines a triple equals operator (===) to give you better error messages. The following code would give you an error indicating only that an assertion failed:

assert(1 == 2)

Using triple equals instead would give you the more informative error message, "1 did not equal 2":

assert(1 === 2)

To test whether a bit of code produces an expected exception, use intercept. Place the expected exception type in square brackets after intercept, and the bit of code in curly braces, like this:

val s = "hi"
intercept[IndexOutOfBoundsException] {
  s.charAt(-1)
}

If the bit of code between the curly braces throws the expected exception, intercept will return it. In the previous example that return value was ignored. If you want to inspect the thrown exception, you can do so like this:

val s = "hi"
val thrown = intercept[IndexOutOfBoundsException] {
  s.charAt(-1)
}
assert(thrown.getMessage === "String index out of range: -1")

If the bit of code between the curly braces throws the wrong exception, or does not throw any exception, you'll get a test failure describing the problem.

Factoring out duplicate code

If you want to factor out duplicate code from tests, mix in BeforeAndAfter, surround the code you want to run before each test by before { ... }, and after each test by after { ... }. Here's an example that just uses before:

import org.scalatest.FeatureSpec
import org.scalatest.GivenWhenThen
import org.scalatest.BeforeAndAfter
import scala.collection.mutable.Stack
 
class ExampleSpec extends FeatureSpec with GivenWhenThen with BeforeAndAfter {
 
  var stack: Stack[Int] = _

  before {
    stack = new Stack[Int]
  }

  feature("The user can pop an element off the top of the stack") {
 
    info("As a programmer")
    info("I want to be able to pop items off the stack")
    info("So that I can get them in last-in-first-out order")

    scenario("pop is invoked on a non-empty stack") {
 
      given("a non-empty stack")
      stack.push(1)
      stack.push(2)
      val oldSize = stack.size
 
      when("when pop is invoked on the stack")
      val result = stack.pop()
 
      then("the most recently pushed element should be returned")
      assert(result === 2)
 
      and("the stack should have one less item than before")
      assert(stack.size === oldSize - 1)
    }
 
    scenario("pop is invoked on an empty stack") {
 
      given("an empty stack")
      when("when pop is invoked on the stack")
      then("NoSuchElementException should be thrown")
      intercept[NoSuchElementException] {
        stack.pop()
      }
 
      and("the stack should still be empty")
      assert(stack.isEmpty)
    }
  }
}

You compile and run it the same way:

$ scalac -cp scalatest-2.2.1.jar ExampleSpec.scala
$ scala -cp scalatest-2.2.1.jar org.scalatest.run ExampleSpec
Run starting. Expected test count is: 2
ExampleSpec:
Feature: The user can pop an element off the top of the stack 
  As a programmer 
  I want to be able to pop items off the stack 
  So that I can get them in last-in-first-out order 
  Scenario: pop is invoked on a non-empty stack
    Given a non-empty stack 
    When when pop is invoked on the stack 
    Then the most recently pushed element should be returned 
    And the stack should have one less item than before 
  Scenario: pop is invoked on an empty stack
    Given an empty stack 
    When when pop is invoked on the stack 
    Then NoSuchElementException should be thrown 
    And the stack should still be empty 
Run completed in 96 milliseconds.
Total number of tests run: 2
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 0, ignored 0, pending 0
All tests passed.

Armed with this knowledge, you can already write real acceptance tests with ScalaTest. To go further, check out the user guide.

ScalaTest is brought to you by Bill Venners, with contributions from several other folks. It is sponsored by Artima, Inc.
ScalaTest is free, open-source software released under the Apache 2.0 license.

Copyright © 2009-2013 Artima, Inc. All Rights Reserved.

artima