Monday, July 26, 2010

ScalaTest, A better way to test.

ScalaTest is a flexible testing framework that allows you to test the way you feel most comfortable testing. For example, it allows you to write tests in a functional manner or in a behavior driven manner. It allows you to write tests for both Scala and Java. It even has traits that can be pulled in to enhance your existing scala JUnit or TestNG tests if you prefer. I have been writing many of my scala tests using both TestNG and JUnit and recently introduced ScalaTest matcher traits into my existing tests. I've come to find that tests as they get more complex tend to get more difficult to read. The ScalaTest matchers go a long way in making your tests easier to read and therefore easier to maintain. This is important when practicing TDD.

Lets start with the following example using the basic JUnit assertions.

package com.foo

import org.junit.Test
import scala.collection.mutable.Map

class FooTest{
val company = Company

@Test
def addsCorrectly {
val jSmith = Person("John", "Smith", 1)
company.hire(jSmith)
assert(company.employeeCount == 1)
assert(company.employees.contains(jSmith))
}
}

object Company {
private val _employees = Map[Long, Person]()
def hire(user:Person) = _employees + (user.id -> user)
def employeeCount = _employees.size
def employees = _employees.values
}

case class Person(firstName:String, lastName:String, id:Long)

As you can see, the JUnit assertions do the job but they just don't read well. It's import to make any code, even test code, easy for anyone else to jump in and quickly understand. The easer code is to read, the easier it is to maintain.

The Hamcrest matcher library provides a variety of matchers that not only provide good functionality but make code a little nicer to read. Lets revisit the example above and rewrite the test method using the Hamcrest matcher library.


package com.foo
import org.junit.Test
import org.hamcrest.Matchers._
import org.hamcrest.MatcherAssert._

class FooTest{
val company = Company

@Test
def addsCorrectly {
val jSmith = Person("John", "Smith", 1)
company.hire(jSmith)
assertThat(company.employeeCount, is(1))
assertThat(company.employees(1), is(jSmith))
}
}

Although the Hamcrest matcher library provides a wide variety of matcher functionality, it still doesn't read as nicely as it could. Lets try this again, this time using the ScalaTest matchers.


package com.foo

import org.scalatest.matchers.ShouldMatchers
import org.scalatest.matchers._
import org.junit.Test

class FooTest extends ShouldMatchers {
val company = Company

@Test
def addsCorrectly {
val jSmith = Person("John", "Smith", 1L)
company.hire(jSmith)

company.employeeCount should be (1)
company.employees should contain value (jSmith)
}
}


There, now that reads nicely. To use the matchers you can simply add the trait to your existing class and you're ready to go. The full source for this example can be found at github.

No comments:

Post a Comment