Summary
The latest version of ScalaTest includes a trait named SpecDasher, which when mixed into a Spec facilitates a more concise expression of specification-style tests. It is deprecated, however, and may be removed in a future release depending on user feedback. What's your opinion?
Advertisement
The basic form of writing a Spec in ScalaTest looks a bit like it does in Ruby's RSpec. You name the subject (class or other entity) you are specifying and testing with "describe" and the actual behavior specifications and accompanying tests (called examples in BDD terminology) with "it." Here's a simple Spec:
import org.scalatest.Spec
import scala.collection.mutable.Stack
class StackSpec extends Spec {
describe("A Stack") {
it("should pop values in last-in-first-out order") {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
it("should throw NoSuchElementException if an empty stack is popped") {
val emptyStack = new Stack[String]
intercept[NoSuchElementException] {
emptyStack.pop()
}
}
}
}
Executing a Spec will produce specification-style output. For example,
running StackSpec from within the Scala interpreter, like this:
scala> (new StackSpec).execute()
Will yield:
A Stack
- should pop values in last-in-first-out order
- should throw NoSuchElementException if an empty stack is popped
With SpecDasher, you can write the same specification a bit more concisely, like this:
import org.scalatest.Spec
import org.scalatest.SpecDasher
import scala.collection.mutable.Stack
class StackDashSpec extends Spec with SpecDasher {
"A Stack" -- {
"should pop values in last-in-first-out order" - {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
"should throw NoSuchElementException if an empty stack is popped" - {
val emptyStack = new Stack[String]
intercept[NoSuchElementException] {
emptyStack.pop()
}
}
}
}
Running StackDashSpec will produce the same specification-style output as the previous one:
scala> (new StackDashSpec).execute()
A Stack
- should pop values in last-in-first-out order
- should throw NoSuchElementException if an empty stack is popped
Trait SpecDasher enables this syntax by offering an implicit conversion from String to a class named Dasher, which has - and -- methods. In ScalaTest 0.9.4, trait Spec already mixes in SpecDasher, because I showed this syntax in Programming in Scala working just in a plain old Spec. ScalaTest 0.9.4 is the version that's illustrated in the book, and I wanted all the code in the book to work, but after the book went to the printer I began to have second thoughts about the dashes style. If I do end up leaving SpecDasher in the API, I will definitely change Spec so that it doesn't already mix in SpecDasher. This way users will have to explicitly invite in these implicit conversion by mixing in SpecDasher explicitly in their own Specs.
However, I am also considering simply dropping SpecDasher entirely, which is why it is deprecated. Although it does clear out just about all clutter around a Spec, leaving just the specification text and the test code, the difference between a describe clause (denoted by two dashes) and an it clause (denoted by one dash) may be less obvious in dashes style. Another advantage that I think explicit "it" clauses may have is that they may make it more likely people will start the spec text with a verb, usually either "should" or "must".
Also, I didn't provide a way in dashes style to "ignore" a test or place a test into groups, because to do so would have added more syntax to learn for both readers and writers of the tests. If you want to do either of those activities, therefore, you'll need to switch back to the default style. Here's how you'd ignore the first test, and place it in the SlowTests group:
ignore("should pop values in last-in-first-out order", SlowTests) {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
Although it is kind of cool to include a class named after one of Santa's reindeer in an API, in general I subscribe to the philosophy that you're not done when you can't think of anything else to add. Rather, you're done when you can't think of anything else to take out. If I do remove SpecDasher, those that like it could still use it by grabbing the source code (which is very small) and mixing it in locally. But I'd like to hear what others think, because I also subscribe to the philosophy that usually you should try to give the people what they want. Please post your opinion in the discussion forum for this blog post.