ScalaTest is a very flexible testing framework for Scala. It supports writing tests in a number of styles. You can read more about ScalaTest in *Testing in Scala*. In this post, we will look at how to get started with the FunSuite style, which offers unit testing functionality that should be familiar to anyone who has worked with xUnit (e.g. JUnit) tests.

## Setting Up The Project

First, be sure to install Simple Build Tool (SBT), since we will be taking advantage of its convenient dependency managements, intelligent compilation, and test framework integration features.

Create an empty directory in which to work (your “work directory”). In this directory, create a file called “build.sbt” with the contents:

1 2 3 |
scalaVersion := "2.10.1" libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test" |

## Defining The Fraction Class

Of course, we will need a body of code to test. Scala source files in the “src/main/scala” directory (rooted in your work directory) will automatically be detected by SBT as source files within our project. With this in mind, let’s create the source file “Fraction.scala” in this directory (you will first have to create the directory tree) and fill it with:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package example.scalatest import annotation.tailrec class ZeroDenominatorException extends IllegalArgumentException( "Zero passed as denominator" ) class Fraction(initialNum: Long, initialDenom: Long) { if (initialDenom == 0) throw new ZeroDenominatorException // Euclid's GCD algorithm // Note: @tailrec will force a compile error if the method can't // be optimized to contain a loop @tailrec private final def gcd(x: Long, y: Long): Long = if (x < y) gcd(y, x) else if (y == 0) x else if (x % y == 0) y else gcd(y, x % y) private val div: Long = gcd(math.abs(initialNum), math.abs(initialDenom)) val num = initialDenom / div val denom = initialDenom / div def +(that: Fraction): Fraction = new Fraction( this.num * that.denom + this.denom * that.num, this.denom * that.denom ) def *(that: Fraction): Fraction = new Fraction( this.num * that.num, this.denom * that.denom ) // String interpolation: $num in the String is replaced with num.toString override def toString: String = s"Fraction($num/$denom)" // Fractions are equal if they have the same num and denom. Fractions cannot // equal objects that are not Fractions. override def equals(other: Any) = other match { case f: Fraction => f.num == this.num && f.denom == this.denom case o: Any => false } } |

You can run `sbt compile`

from your work directory to compile the Fraction class. If you’ve just installed SBT, the first compile will take quite a long time, since SBT will first download the Scala compiler and runtime libraries, as well as the ScalaTest framework. Once the compilation completes, you can run `sbt console`

to get a Scala REPL (interpreter) session with the Fraction class on the classpath (you will have to import it first, though, by running: `import example.scalatest._`

).

## FunSuite Template

Now that we have a moderately-featured self-reducing Fraction class, we can begin to look at actually writing tests. A general template for a group of FunSuite tests looks like:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import org.scalatest.FunSuite class FooSuite extends FunSuite { test("A Foo does bar") { // Test Code } test("A Foo doesn't baz") { // Test Code } ignore("A Foo does bippy") { // Ignored Test Code } } |

As you might guess, each call to test denotes a test in the test suite. Replacing test with ignore tells ScalaTest not to run a given test, but it allows you to leave the test’s code in place. This is useful when you are first building the test suite or when you want to temporarily ignore the results of a given test while working on code. Both test and ignore take a description String and a block, which represents the actual body of the test.

## Tools For Writing Tests

Within a test, we have a couple of tools available to us. First and foremost is the classic assert. This function is called with a Boolean-returning argument. If the Boolean argument is true, the assert is a no-op. If it’s false, however, the test fails. The only twist with ScalaTest is that if you test for equality inside an assert, you should use the `===`

(triple-equals) operator, rather than the standard `==`

operator, as it is slightly better-behaved (it produces better error messages on test failure and compares Arrays by comparing their contents). Here’s an example that uses asserts:

1 2 3 4 5 6 7 8 9 10 |
test("1 should equal 1) { val x = 1 val y = 1 assert(x === y) } test("1 should not equal 2) { // Alternate two-param assert, takes a message for failures: assert(1 != 2, s"value $x equaled $y") } |

Second, we can use a method called `intercept`

, which allows us to assert that a block of code throws a specific type of exception. It is used like this:

1 2 3 4 5 6 |
test("Using intercept to check for divide-by-zero") { val x = 0 intercept[ArithmeticException] { 1 / x } } |

Note that the test fails if an exception of the intercepted type is not thrown:

1 2 3 4 5 6 |
test("Using intercept to catch a NullPointerException (this one will fail)") { val x = 4 intercept[NullPointerException] { x.toString } } |

Also, note that a non-failing call to `intercept`

returns the intercepted exception, allowing you to capture it in a variable and inspect it, if you wish:

1 2 3 4 5 |
test("Using intercept to catch a NullPointerException") { val x = null val npe = intercept[NullPointerException] { x.toString } assert(npe.isInstanceOf[NullPointerException]) } |

## Writing The Test Suite

Let’s create a test suite for our `Fraction`

class. Scala source files in the src/test/scala directory will be considered test source code by SBT. Create a file called `FractionSuite.scala`

in this directory with the contents:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import org.scalatest.FunSuite import example.scalatest._ class FractionSuite extends FunSuite { test("Fractions should be equal if they share a numerator and a denominator") { assert(new Fraction(1, 2) === new Fraction(1, 2)) assert(new Fraction(2, 5) === new Fraction(2, 5)) } test("Fractions should reduce correctly") { assert(new Fraction(2, 4) === new Fraction(1, 2)) assert(new Fraction(8, 24) === new Fraction(1, 3)) assert(new Fraction(75, 100) === new Fraction(3, 4)) assert(new Fraction(11, 77) === new Fraction(1, 7)) } test("Fractions should add correctly") { val half = new Fraction(1, 2) val third = new Fraction(1, 3) assert(half + third === new Fraction(5, 6)) } test("Fraction should multiply correctly") { val half = new Fraction(1, 2) val third = new Fraction(2, 3) assert(half * third === new Fraction(1, 3)) } test("Fractions should disallow a zero denominator") { intercept[ZeroDenominatorException] { new Fraction(2, 0) } } test( "Fractions should be not change value when their numerator and\n"+ " denominator are negated" ) { assert(new Fraction(-1, 2) === new Fraction(1, -2)) assert(new Fraction(1, 2) === new Fraction(-1, -2)) } } |

## Running The Test Suite

We can run our test suite from the work directory with “sbt test.” Do this now, and you should get an output like the following:

1 2 3 4 5 6 7 8 9 10 |
FractionSuite: - Fractions should be equal if they share a numerator and a denominator - Fractions should reduce correctly - Fractions should add correctly - Fraction should multiply correctly - Fractions should disallow a zero denominator - Fractions should be not change value when their numerator and denominator are negated *** FAILED *** Fraction(-1/2) did not equal Fraction(1/-2) (FractionSuite.scala:38) Failed: : Total 6, Failed 1, Errors 0, Passed 5, Skipped 0 |

Oops! It looks like one of our tests failed! Unlike real fractions, our `Fractions`

are not equal if their numerator and denominator have their signs flipped. The fix is to change the definitions of `num`

and `denom `

in our `Fraction`

class to:

1 2 3 4 5 |
// Note: math.signum(x) == x / math.abs(x) forall x != 0 // We use it here to make sure that the negative sign (if there is one) // always goes on the numerator val num = math.signum(initialDenom) * initialNum / div val denom = math.abs(initialDenom) / div |

Now, “sbt test” should produce:

1 2 3 4 5 6 7 8 9 |
FractionSuite: - Fractions should be equal if they share a numerator and a denominator - Fractions should reduce correctly - Fractions should add correctly - Fraction should multiply correctly - Fractions should disallow a zero denominator - Fractions should be not change value when their numerator and denominator are negated Passed: : Total 6, Failed 0, Errors 0, Passed 6, Skipped 0 |

## Conclusion

ScalaTest is a full-featured testing framework, having over half a dozen additional testing styles, an English-like domain-specific language for creating executable specifications, and a number of tools for working with fixture objects (objects that require setup and tear down for each test). For small-to-medium projects that want to get off the ground fast, however, FunSuite and the tools described in this post should more than suffice.

## Safari Books Online has the content you need

Check out these Scala books available from Safari Books Online:

Testing in Scala shows you how to create tests with ScalaTest and the Specs2—two of the best testing frameworks available—and how to run your tests in the Simple Build Tool (SBT) designed specifically for Scala projects. | |

Scala in Action is a comprehensive tutorial that introduces Scala through clear explanations and numerous hands-on examples. Because Scala is a rich and deep language, it can be daunting to absorb all the new concepts at once. This book takes a “how-to” approach, explaining language concepts as you explore familiar programming challenges that you face in your day-to-day work. | |

This book takes a step-by-step tutorial approach to teaching you Scala. Starting with the fundamental elements of the language, Programming in Scala introduces functional programming from the practitioner’s perspective, and describes advanced language features that can make you a better, more productive developer. | |

Scala in Depth is a unique new book designed to help you integrate Scala effectively into your development process. By presenting the emerging best practices and designs from the Scala community, it guides you though dozens of powerful techniques example by example. |

### About the author

Chris Hodapp is nearing the completion of a Master’s degree in Computer Science at the University of Alabama, the same school from which he holds an undergraduate CS degree. Since completing a Google Summer of Code with the Scala team, he has spent most of his free time looking for ways to do interesting things both with and to Scala. Chris can be reached on Twitter, where he is @clhodapp. |