A short Introduction to ScalaTest

January 13th, 2013 by

ScalaTest is an excellent framework to write concise, readable tests for your Scala or Java code with less effort.

In addition it integrates well with a variety of frameworks like JUnit, TestNG, Ant, Maven, sbt, ScalaCheck, JMock, EasyMock, Mockito, ScalaMock, Selenium, Eclipse, NetBeans, and IntelliJ.

In the following short tutorial we’re going to write some tests using ScalaTest exploring features like rich matchers, BDD syntax support or web tests using Selenium/Webdriver.

 

SBT Dependencies

We need just one dependency here: scalatest – the other dependencies are needed for the JUnit and Selenium examples.

Just add the following dependencies to your build.sbt;

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M5" % "test"
 
libraryDependencies += "junit" % "junit" % "4.11" % "test"
 
libraryDependencies += "org.seleniumhq.selenium" % "selenium-java" % "2.25.0" % "test"

Simple Test using FlatSpec

I have used Roy Osherove’s String Calculator Coding Kata here to write some tests in a test-driven manner.

By the way .. if you don’t like the keyword should and you want to replace it with must, simply use the trait MustMatchers

package com.hascode
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
 
class StringCalculatorSpec extends FlatSpec with ShouldMatchers {
 
 "calc" should "return 0 given an emtpy string" in {
 calc("", "") should equal(0)
 }
 
 it should "return 1 given a string '1' solely" in {
 calc("1", "") should equal(1)
 calc("", "1") should equal(1)
 }
 
 it should "return a 2 given two strings '1'" in {
 calc("1", "1") should equal(2)
 }
 
 it should "return a 3 given a '1' and a string '2'" in {
 calc("1", "2") should equal(3)
 }
 
 it should "return a 8 given three strings '1', '3', '4'" in {
 calc("1", "3", "4") should equal(8)
 }
 
 it should "return 0 if all strings are empty" in {
 calc("", "") should equal(0)
 calc("", "", "") should equal(0)
 calc("", "", "", "") should equal(0)
 calc("", "", "", "", "") should equal(0)
 }
 
 it should "return a 64 given five strings '1','3','7','20','33'" in {
 calc("1", "3", "7", "20", "33") should equal(64)
 }
 
 def calc(in: String*): Int = {
 var ret = 0
 in.filter(_ != "").foreach(ret += _.toInt)
 ret
 }
}

Running the test should produce a similar output:

$> sbt
> test-only com.hascode.StringCalculatorSpec
[info] StringCalculatorSpec:
[info] calc
[info] - should return 0 given an emtpy string
[info] - should return 1 given a string '1' solely
[info] - should return a 2 given two strings '1'
[info] - should return a 3 given a '1' and a string '2'
[info] - should return a 8 given three strings '1', '3', '4'
[info] - should return 0 if all strings are empty
[info] - should return a 64 given five strings '1','3','7','20','33'
[info] Passed: : Total 7, Failed 0, Errors 0, Passed 7, Skipped 0
[success] Total time: 0 s, completed Jan 11, 2013 11:12:56 AM

Matchers

There are several matchers available to spice up your life and I am going to explain a short subset of the existing matchers here.

A complete and detailed documentation for the matchers can be found in the ScalaTest documentation.

Equality

4 must equal(4)
"foo" must equal("foo")
List("foo", "bar", "baz") must equal(List("foo", "bar", "baz"))
(1, "foo") must equal((1, "foo"))

Sizes, Lengths

"foo" must have length (3)
List(1, 2, 3, 4) must have length (4)
Array('A', 'B') must have length (2)
 
List(1, 2, 3, 4) must have size (4)
Array('A', 'B') must have size (2)

Checking Strings

"foobarbaz" must startWith("foo")
"foobarbaz" must endWith("baz")
"foobarbaz" must include("bar")
 
"foobarbaz" must startWith regex ("f[o]+")
"foobarbaz" must endWith regex ("[ba]{2}z")
"foobarbaz" must include regex ("foo[\\w]{3}baz")
"foobarbaz" must fullyMatch regex ("\\w{1}oobarbaz")

Number Comparison

7 must be < (8)
7 must be > (0)
6 must be <= (7)
7 must be >= (6)
13.43 must equal(13.43)
13.43 must be(13.4 plusOrMinus 0.4)

Checking boolean properties

If you want to check an object’s boolean properties, there is a nice short form for you in scala:

List[Int]() must be('empty) // calls list.isEmpty
new File("/tmp") must be('directory) // calls file.isDirectory

Testing object identity

var foo = List(1, 2)
var bar = foo
bar must be theSameInstanceAs (foo)

Collections

List(1, 2, 3, 4) must contain(2)
Array("foo", "bar", "baz") must contain("bar")
var users = Map(1 -> "Joe", 2 -> "Lisa", 3 -> "Dr. Evil")
users must contain key (2)
users must contain value ("Dr. Evil")

Class Properties

case class Recipe(name: String, ingredients: List[String], bakingMinutes: Int)
 
val cookieRecipe = Recipe("cookieRecipe", List("Flour", "Sugar", "Eggs", "Love"), 30)
cookieRecipe must have(
 'name("cookieRecipe"),
 'ingredients(List("Flour", "Sugar", "Eggs", "Love")),
 'bakingMinutes(30)
)

Negation

7 must not be >(8)
List(1, 2, 3, 4) must not be ('empty)

Combined expressions with and/or

Don’t forget to surround the expression with parantheses .. e.g.  “users must have size(3) and contain key(3)” would be wrong.

users must (have size (3) and contain key (3))
users must (contain value ("Mr. X") or contain value ("Joe"))

JUnit Support

Sometimes one need to run the test files using JUnit because of a tools that depends on that framework (e.g. Infinitest) – luckily this can be done with ease.

If you’re simply missing the well known JUnit view in Eclipse, please take a look at the Test Runner that comes with the Scala IDE for Eclipse first!

package com.hascode
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
 
@RunWith(classOf[JUnitRunner])
class StringCalculatorSpec extends FlatSpec with ShouldMatchers {
 // [..]
}

Now you’re able to run your tests using the JUnit runner:

Running ScalaTest with JUnit in Eclipse

Running ScalaTest with JUnit in Eclipse

Infinitest also works well again

Infinitest Plugin for Eclipse

Infinitest Plugin for Eclipse

BDD / Given-When-Then Syntax

Writing tests in the classical BDD style with given-when-then is easy and there is a nice trait, GivenWhenThen to support this syntax.

package com.hascode
import org.scalatest.GivenWhenThen
import org.scalatest.FlatSpec
import org.scalatest.matchers.MustMatchers
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
 
class BDDSyntaxExample extends FlatSpec with MustMatchers with GivenWhenThen {
 "A HashMap" should "allow an item to be added" in {
 Given("an empty hashmap of type Int->String")
 val map = collection.mutable.Map[Int, String]()
 
 When("an item is added")
 map += (1 -> "Joe")
 
 Then("the map should have size 1")
 map must have size (1)
 
 And("the map must contain the item added")
 map must (contain key (1) and contain value ("Joe"))
 
 info("Adding items seems to be working")
 }
}

Running the tests produces the following output:

$> sbt
> test-only com.hascode.BDDSyntaxExample
[info] Compiling 1 Scala source to /data/project/scala-test-examples/target/scala-2.9.1/test-classes...
[info] BDDSyntaxExample:
[info] A HashMap
[info] - should allow an item to be added
[info]   + Given an empty hashmap of type Int->String
[info]   + When an item is added
[info]   + Then the map should have size 1
[info]   + And the map must contain the item added
[info]   + Adding items seems to be working
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0

Selenium WebDriver Integration

If you need to test your web application, ScalaTest offers a nice integration of Selenium Webdriver here and allows you to write your acceptance tests in a short and concise way.

Well-written and more detailed information can be found in the ScalaTest documentation.

In the following examples, we’re testing my blog’s search and validate the result of the search afterwards.

package it
import org.junit.runner.RunWith
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.WebDriver
import org.scalatest.junit.JUnitRunner
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.selenium.WebBrowser
import org.scalatest.FlatSpec
import org.scalatest.GivenWhenThen
import org.openqa.selenium.htmlunit.HtmlUnitDriver
 
class WebsiteSpec extends FlatSpec with GivenWhenThen with ShouldMatchers with WebBrowser {
 implicit val webDriver: WebDriver = new HtmlUnitDriver
 
 "My Website" should "search for a given term" in {
  go to ("http://www.hascode.com/")
  title should be("hasCode.com")
  click on id("s")
  textField("s").value = "lucene"
  submit()
  title should include regex ("hasCode.com.+Search Results.+lucene")
  pageSource should include("Lucene Snippets: Index Stats")
  pageSource should include("Lucene Snippets: Faceting Search")
  pageSource should include("Hibernate Search Faceting: Discrete and Range Faceting by Example")
 }
}
$>sbt
> test-only it.WebsiteSpec
[info] WebsiteSpec:
[info] My Website
[info] - should search for a given term
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 5 s, completed Jan 12, 2013 5:03:48 PM

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository or clone it using Git:

git clone https://bitbucket.org/hascode/scala-test-examples.git

Resources

Tags: , , , , , , , , , , ,

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 80,968 bad guys.

Search
Categories