ScalaTest 102: Writing TDD style unit tests with ScalaTest

Problem: You want to use test-driven development (TDD) style unit tests in your Scala applications, and need to see examples of how to write them.

Solution

Have your test classes extend the ScalaTest FunSuite, and then write your tests. Because most tests involve a setup and teardown process, you’ll usually also want to add in the BeforeAndAfter trait as well.

The following example demonstrates a simple set of TDD tests for a Pizza class:

package com.acme.pizza

import org.scalatest.FunSuite
import org.scalatest.BeforeAndAfter

class PizzaTests extends FunSuite with BeforeAndAfter {
  
  var pizza: Pizza = _

  before {
    pizza = new Pizza
  }

  test("new pizza has zero toppings") {
    assert(pizza.getToppings.size == 0)
  }

  test("adding one topping") {
    pizza.addTopping(Topping("green olives"))
    assert(pizza.getToppings.size === 1)
  }

  // mark that you want a test here in the future
  test ("test pizza pricing") (pending)

}

Assuming that you’re using SBT with the default directory structure, this file should be named something like PizzaTests.scala, and should be placed under the src/test/scala directory structure, preferably in a directory named src/test/scala/com/acme/pizza. (The file can be placed anywhere under the src/test/scala directory, but I prefer to match the package and directory names.)

The accompanying Pizza.scala and Topping.scala files should be placed in the src/main/scala/com/acme/pizza directory. Here’s the source code for the Pizza class:

package com.acme.pizza

import scala.collection.mutable.ArrayBuffer

class Pizza {
  
  private val toppings = new ArrayBuffer[Topping]
  
  def addTopping (t: Topping) { toppings += t}
  def removeTopping (t: Topping) { toppings -= t}
  def getToppings = toppings.toList
  
  def boom { throw new Exception("Boom!") }
}

Here’s the Topping class:

package com.acme.pizza

case class Topping(name: String)

With these classes in place, run the tests with the SBT test command from your shell prompt, in your project’s root directory:

$ sbt test

Your output will look like this:

[info] Compiling 1 Scala source to 
       /Users/Al/ScalaTests/target/scala-2.10/classes...
[info] Compiling 1 Scala source to 
       /Users/Al/Scala/ScalaTests/target/scala-2.10/test-classes...
[info] PizzaTests:
[info] - new pizza has zero toppings
[info] - adding one topping
[info] - test pizza pricing (pending)
[info] Passed: : Total 3, Failed 0, Errors 0, Passed 2, Skipped 1

Although there are other ways to run your tests, I’m a believer in using the tools of a language, so I recommend using SBT. See the ScalaTest guide to running your tests.

Discussion

The Scaladoc for the FunSuite class describes the class like this:

A suite of tests in which each test is represented as a function value. The "Fun" in FunSuite stands for "function."

The before method lets you do any setup work that needs to be performed before each test is run. Similarly, the after method lets you perform any teardown work that should be performed after each test.

If you’ve used JUnit or other testing frameworks, these tests will look familiar. Just describe your test with a unique string, and that string will be used in the printed output. Make sure your strings are unique, or an exception will be thrown.

The basic assert method is familiar, but it also has more power, as you’ll see in other recipes.

The pending feature shown in the last test is very helpful:

test ("test pizza pricing") (pending)

This is a nice way of noting that you need to add a test in the future, but for one reason or another you’re not adding that test right now. As you saw, this ends up in the printed output like this:

[info] - test pizza pricing (pending)

See Also