O'Reilly logo

Testing in Scala by Daniel Hinojosa

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. Setup

Simple Build Tool (hereafter called SBT) is a build tool specifically used for Scala projects. SBT uses actual Scala for its build language. SBT runs and compiles Java and Scala files and uses the Maven directory structure. SBT also has a interesting feature called triggered executions that will recompile code or run tests whenever you change a file, among other great features. SBT supports multitiered projects and is highly extensible, thanks to its plug-in infrastructure. Just like other build tools, it can package projects and is also extensible by allowing the end user to add more features to the build tool. But unlike other build tools it has a built-in Read-Eval-Print-Loop (REPL) interactive console. The interactive console in SBT can also import a project’s class files so that you can experiment with existing project code.

Setup in Mac OS X, Mac OS X Lion, and Linux

Setting up SBT is fairly straightforward. Download the sbt JAR file from the website and place it in the ~/bin directory, which you may have to create in your home directory. Next, create a shell file called sbt that will hold the command to launch XSBT:

java -jar -Dfile.encoding=UTF8 -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled
    -XX:MaxPermSize=256m `dirname $0`/sbt-launch.jar "$@"

Setup in Windows

Setting up SBT in Windows is also straightforward. Create a .bat file called sbt.bat in a directory of your choosing, and write the following contents there:

set SCRIPT_DIR=%~dp0
java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*

Next, be sure that your current .bat file is located in a directory that is currently mapped to your path in your environment. Depending on your system, add the directory where you placed sbt to your path.

For Windows XP, right-click My Computer and then click Properties. Once in the System Properties window, click the Advanced Tab. Click the Environment Variables button. This should bring up the Environment Variables dialog. You will see two sets of environment variables: User Variables and System Variables. User Variables take effect only in your profile, not the entire system. System variables are for system-wide settings. If your account is an Administration account and has full access to your machine, you can add a system variable.

Using SBT

To get started, create a folder for your project. For the project in this book we will create a folder (directory) called testingscala. Change into that directory and run the ./sbt program. An Internet connection is required for this step, since some dependencies are required to initialize your directory. When you are finished, you should see the > SBT prompt:

$ mkdir testingscala
$ cd testingscala
$ sbt
:: retrieving :: org.scala-tools.sbt#boot-app
    confs: [default]
        1 artifacts copied, 0 already retrieved (838kB/31ms)
Getting Scala 2.9.1 (for sbt)...
:: retrieving :: org.scala-tools.sbt#boot-scala
        confs: [default]
        3 artifacts copied, 0 already retrieved (15178kB/292ms)
Getting org.scala-tools.sbt sbt_2.9.1 0.10.1 ...
:: retrieving :: org.scala-tools.sbt#boot-app
        confs: [default]
        36 artifacts copied, 0 already retrieved (6414kB/103ms)
[info] Set current project to default-362242 (in build file:/home/danno/.sbt/plugins/)
[info] Set current project to default-cef86a (in build file:/home/danno/testingscala/)
>

Next, type exit at the SBT prompt, since you need to return to your operating system prompt to set up your folder organization and your build file.

SBT Folder Organization

SBT adheres to the Maven standard of folder organization. All source production files go into src/main and all test files go into src/test. For our small introduction to test-driven development, using Scala tests on Java production code, all we need is a src/test/scala directory and a src/main/java directory.

The Build File

The build file is a plain Scala file, placed in the root of the project, called build.sbt. Here the developer specifies basic attributes such as the project name and version, the Scala version, and all the dependencies required. SBT makes use of the Maven-style repositories to download the binary and source JARs required for your project. One obvious benefit to that procedure is that you never have to commit large JAR file dependencies to your project, since they are automatically downloaded when you run update.

Once you have an SBT project ready to go, make sure it uses the latest ScalaTest dependency. In the build.sbt file, include your scalatest dependency. This will download the scalatest library and place it on the test classpath automatically.

name := "Testing Scala"

version := "1.0"

scalaVersion := "2.9.2"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test"

org.scalatest will look in the maven and scala central repositories by default to look for the dependency scalatest_2.9.2. The reason it knows to look for scalatest_2.9.2 and not plain scalatest is because the line includes two percent signs %% in the address. %% will append an underscore and the scala version number to the name of the library. If you don’t want SBT to control the scala version and wish to do it yourself, you can specify your own version using % instead of %%.

Here is the last line of the build.sbt file written differently, but accomplishing the same goal.

libraryDependencies += "org.scalatest" % "scalatest_2.9.2" % "1.8" % "test"

Also note that the declaration consains a scope for our dependency. This would ensure that all classes from this dependency will be loaded only for testing. It will not be used during compilation or packaging.

Now run reload within sbt, which will compile any sbt files, and update, which will download and set up the dependencies. At the command prompt, it is possible to call run sbt reload update to combine operations. The command will both reload build.sbt with the latest changes and update all the dependencies from any repositories.

To run SBT in an interactive shell of its own, run sbt, and then do the reload and update from within the sbt console as in the following example.

> reload
[info] Set current project to default-ca9689 (in build file:/home/danno
    /development/testingscala/project/plugins/)
[info] Set current project to default-1f0130 (in build file:/home/danno
    /development/testingscala/)
> update
[info] Updating {file:/home/danno/development/testingscala/}default-1f0130...
[info] Done updating.
[success] Total time: 9 s, completed Sep 25, 2011 4:01:06 PM
>

That’s all it takes to get a fairly simple setup ready for a standard project with testing.

Note

Testing Java using Scala also might give you an excuse to slip some Scala in at work or in your personal projects. Testing in Scala is a great way to learn the language, and if you do it at work in a project you are familiar with, you can see the benefits of Scala in your own domain.

We will place our tests in the src/test/scala folder of the project, and we’ll eventually place our production code in the src/main/scala directory.

About Our Examples

Since this book’s focus is on testing, particularly in Scala, I am going to keep the production/target code simple. Don’t assume that I’m endorsing the use of TDD just on simple code. TDD is amazing with the most challenging of code, and it’s indispensible in times that require some mental clarity and focus.

Our code samples will be a mix of something fun and something relevant. The samples will include a digital jukebox with albums (yes, even in the digital era I still call them albums). Each album will with have an artist associated with it. The artist class will have a band subclass, and each Band will have a collection of artist associated with it. The collections we use for these examples will be the powerful lists, sets, maps, and arrays that come with Scala.

Of course, what good is a digital jukebox without some persistence? Each song will need to persisted into some sort of database, whether it is a classic SQL database or a newfangled NoSQL database. The reason we require an example of storing to a database is because we need it for mocking using a framework such as Mockito, EasyMock, or ScalaMock. If you are unfamiliar with the topic of mocking, you can learn about it in Chapter 5.

Creating Our Examples Using TDD, ScalaTest, and SBT

Let’s start SBT triggered execution. Triggered execution in SBT makes TDD exciting and intuitive, since it runs every time there is a change to the code. Because one of TDD’s most basic tenets is that unit testing needs to be a constantly repeateable and constantly evolving, SBT will run that test for you without added involvement.

To start triggered execution, run SBT using the command sbt on your Mac or Linux box, or sbt.bat on your Windows system. If you do so successfully you should receive an sbt> prompt. At this prompt, type ~test. SBT is now listening for your next save. Every time you save a production file, and every time you save your test, SBT will wake up and run your tests again. This is a very nice tool because it keeps your mind focused on your test and your production file. This focused development will not only help you to achieve your goals faster, but will produce a better product in the long run.

We will create our first simple test in the src/test/scala/com/oreilly/testingscala/ folder, calling the test AlbumTest.

Note

Package folders are a matter of taste. If you want to create a Scala file without the com, oreilly, and testingscala folders, you are certainly free to do so. You can also opt to just have an AlbumTest file in the src/test/scala folder while keeping the same package name. The choice is up to you and/or your team. All class files will be expanded into their corresponding folders in the target/scala-2.9.2/test-classes folder after compilation.

src/test/scala/com/oreilly/testingscala/AlbumTest.scala

package com.oreilly.testingscala

import org.scalatest.FunSpec
import org.scalatest.matchers.ShouldMatchers

class AlbumTest extends FunSpec with ShouldMatchers  {
   describe("An Album") {
       it ("can add an Artist object to the album") {
           val album = new Album("Thriller", 1981,
              new Artist("Michael", "Jackson"))
       }
   }
}

This is a small example of a FunSpec in ScalaTest. We will cover ScalaTest in its own chapter. For this example, FunSpec is a trait, which is a class in Scala that has its own concrete implementation but can be mixed into another class rather like Java interfaces. Our trait FunSpec allows us to think of our test as a behavior-driven test. Behavior-driven development is a refinement on test-driven development. Behavior-driven development provides storied, easy-to-read test reporting to help include other stakeholders such as QA teams in your testing process. When a test has more fluid descriptions of a test, it is also easier to debug since the test-driven developer is able to be expressive and describe the exact intent of the test. When the bug occurs, it is easier to decipher “test that a withdrawal cannot occur when an account has zero funds” than to understand a JUnit-style “testWithdrawalWithZeroFunds.” This expressiveness in describing the test is what Dan North, in his introduction to Behavior Driven Development, refers to as bringing the business vocabulary into the codebase.

When you save AlbumTest for the first time, the sbt console shows that SBT’s triggered execution ran when you saved changes.

> ~test
1. Waiting for source changes... (press enter to interrupt)
[info] Compiling 1 Scala source to /home/danno/testing_scala_book.git
    /testingscala/target/scala-2.9.2/test-classes...
[error] /home/danno/testing_scala_book.git/testingscala/src/test/scala
    /com/oreilly/testingscala/AlbumTest.scala:9: not found: type Album
[error]            val album = new Album("Thriller", 1981,
[error]                            ^
[error] one error found
[error] {file:/home/danno/testing_scala_book.git/testingscala/}default-cef86a
    /test:compile: Compilation failed
[error] Total time: 1 s, completed Nov 29, 2011 1:35:43 PM

Our test failed because we haven’t created an Album or Artist class yet. But this is common in test-driven development. We create the test first by creating code that resembles how we want to use the class. Kent Beck in Test Driven Development: By Example recommends creating a list of tests before writing code. The point is to first establish the test and make it fail, which we have done successfully.

The next course of action is to satisfy this test to make it pass (maybe). We do so by creating some classes.

src/main/scala/com/oreilly/testingscala/Album.scala

package com.oreilly.testingscala

class Album (val title:String, val year:Int, val artist:Artist)

src/main/scala/com/oreilly/testingscala/Artist.scala

package com.oreilly.testingscala

class Artist (val firstName: String, val lastName: String)

If you are unfamiliar with Scala, notice that these class declarations are shorter than normal and have and a sprinkle of keywords that you may not be expecting. Also, the variable declarations come before their types are declared. The val keyword creates getters for each of the field names, but no setters. Our class in this case is immutable (unable to be changed), which is preferred in Scala, but it is not a hard-and-fast rule. Scala also allows for mutability. For more information about the Scala language and immutability, please refer to Programming Scala by Dean Wampler and Alex Payne.

After adding the two classes, when we look at SBT’s response we are rewarded with green text (which unfortunately doesn’t show up too well if you are reading a black-and-white book). In our response we have an AlbumTest one assertion that has passed. We can add Artist object to the album.

4. Waiting for source changes... (press enter to interrupt)
[info] Compiling 1 Scala source to /home/danno/testing_scala_book.git/testingscala/target
    /scala-2.9.2/classes...
[info] Compiling 2 Scala sources to /home/danno/testing_scala_book.git/testingscala/target
    /scala-2.9.2/classes...
[info] Compiling 1 Scala source to /home/danno/testing_scala_book.git/testingscala/target
    /scala-2.9.2/test-classes...
[info] AlbumTest:
[info] An Album
[info] - can add a Artist object to the album
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 4 s, completed Nov 29, 2011 2:18:38 PM
5. Waiting for source changes... (press enter to interrupt)

Our result now will show behavior-driven development’s storied reporting in plain English: “An album can add an Artist object to the album.”

The last statement of our triggered executions states that it is waiting for more changes. It also gives you instructions for exiting the triggered execution by pressing Enter. Whenever there is any change to any of the code, the test will run again, giving you the immediate feedback that you need for effective test-driven development.

In the following sections we will cover how to create loads of fake data with ScalaCheck, and how to use mocking within Scala using EasyMock, Mockito, and one of the latest mocking frameworks, ScalaMock, formerly known as Mockito. Later we will cover more about SBT, ScalaTest, and its competitor Specs2. We will cover some gotchas with Scala development, including some of the harder concepts to test such as Scala objects.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required