ScalaTest provides multiple testing styles to accommodate different preferences and project requirements. Each style offers a different syntax and organization approach while sharing the same underlying execution engine.
Function-based testing style with named test functions.
abstract class AnyFunSuite extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Register a test with the given spec text and test function value
*/
protected def test(testName: String)(testFun: => Any): Unit
/**
* Register a test to ignore with the given spec text
*/
protected def ignore(testName: String)(testFun: => Any): Unit
}Usage Example:
import org.scalatest.funsuite.AnyFunSuite
class ExampleFunSuite extends AnyFunSuite {
test("pop is invoked on a non-empty stack") {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
val oldSize = stack.size
val result = stack.pop()
assert(result === 2)
assert(stack.size === oldSize - 1)
}
ignore("pop is invoked on an empty stack") {
val emptyStack = new Stack[Int]
intercept[IllegalArgumentException] {
emptyStack.pop()
}
}
}Behavior-driven development style with "should" syntax.
abstract class AnyFlatSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Class that supports the registration of tests in shorthand form
*/
protected final class FlatSpecStringWrapper(string: String) {
def should(testFun: => Any): Unit
def must(testFun: => Any): Unit
def can(testFun: => Any): Unit
}
/**
* Implicitly converts strings to FlatSpecStringWrapper for test registration
*/
protected implicit def convertToFlatSpecStringWrapper(o: String): FlatSpecStringWrapper
/**
* Register a test to ignore
*/
protected def ignore: IgnoreWord
}Usage Example:
import org.scalatest.flatspec.AnyFlatSpec
class ExampleFlatSpec extends AnyFlatSpec {
"A Stack" should "pop values in last-in-first-out order" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
it should "throw IllegalArgumentException if pop is invoked on an empty stack" in {
val emptyStack = new Stack[String]
intercept[IllegalArgumentException] {
emptyStack.pop()
}
}
ignore should "be ignored" in {
// This test will be ignored
}
}Ruby RSpec-like nested describe/it syntax.
abstract class AnyFunSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Register a description of the subject being specified and tested
*/
protected def describe(description: String)(fun: => Unit): Unit
/**
* Register a specification/test for the subject being described
*/
protected def it(specText: String)(testFun: => Any): Unit
/**
* Register a test to ignore
*/
protected def ignore(testName: String)(testFun: => Any): Unit
}Usage Example:
import org.scalatest.funspec.AnyFunSpec
class ExampleFunSpec extends AnyFunSpec {
describe("A Stack") {
describe("when not empty") {
it("should remove and return the top item on pop") {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
}
}
describe("when empty") {
it("should throw IllegalArgumentException on pop") {
val emptyStack = new Stack[Int]
intercept[IllegalArgumentException] {
emptyStack.pop()
}
}
}
}
}Specification style with subject-verb-object structure.
abstract class AnyWordSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Class that supports the registration of subjects
*/
protected final class WordSpecStringWrapper(string: String) {
def when(f: => Unit): Unit
def which(f: => Unit): Unit
def should(f: => Unit): Unit
def must(f: => Unit): Unit
def can(f: => Unit): Unit
}
/**
* Implicitly converts strings to WordSpecStringWrapper
*/
protected implicit def convertToWordSpecStringWrapper(s: String): WordSpecStringWrapper
}Usage Example:
import org.scalatest.wordspec.AnyWordSpec
class ExampleWordSpec extends AnyWordSpec {
"A Stack" when {
"not empty" should {
"return the last item added on pop" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
}
}
"empty" should {
"throw IllegalArgumentException on pop" in {
val emptyStack = new Stack[Int]
intercept[IllegalArgumentException] {
emptyStack.pop()
}
}
}
}
}Free-form specification style allowing arbitrary nesting.
abstract class AnyFreeSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Class that supports the registration of free-form text with nested tests/scopes
*/
protected final class FreeSpecStringWrapper(string: String) {
def in(f: => Any): Unit
def is(f: => Unit): Unit
def ignore(f: => Any): Unit
}
/**
* Implicitly converts strings to FreeSpecStringWrapper
*/
protected implicit def convertToFreeSpecStringWrapper(s: String): FreeSpecStringWrapper
}Usage Example:
import org.scalatest.freespec.AnyFreeSpec
class ExampleFreeSpec extends AnyFreeSpec {
"A Stack" - {
"when not empty" - {
"should pop values in LIFO order" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
}
"when empty" - {
"should throw on pop" in {
val emptyStack = new Stack[Int]
intercept[IllegalArgumentException] {
emptyStack.pop()
}
}
}
}
}Property-based testing style for specifying properties that should hold.
abstract class AnyPropSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Register a property-based test
*/
protected def property(testName: String)(testFun: => Any): Unit
/**
* Register a property-based test to ignore
*/
protected def ignore(testName: String)(testFun: => Any): Unit
}Usage Example:
import org.scalatest.propspec.AnyPropSpec
import org.scalatest.prop.PropertyChecks
class ExamplePropSpec extends AnyPropSpec with PropertyChecks {
property("list size should equal the number of elements added") {
forAll { (list: List[Int]) =>
val buffer = scala.collection.mutable.ListBuffer.empty[Int]
list.foreach(buffer += _)
assert(buffer.size === list.size)
}
}
property("reversing a list twice should give the original list") {
forAll { (list: List[String]) =>
assert(list.reverse.reverse === list)
}
}
}Acceptance testing style with Given-When-Then structure.
abstract class AnyFeatureSpec extends Suite with TestSuite with Assertions with TestRegistration {
/**
* Register a feature (user story or requirement)
*/
protected def feature(description: String)(fun: => Unit): Unit
/**
* Register a scenario within a feature
*/
protected def scenario(description: String)(fun: => Any): Unit
/**
* Register a scenario to ignore
*/
protected def ignore(description: String)(fun: => Any): Unit
/**
* Provides Given step syntax
*/
protected def Given(description: String): Unit
/**
* Provides When step syntax
*/
protected def When(description: String): Unit
/**
* Provides Then step syntax
*/
protected def Then(description: String): Unit
/**
* Provides And step syntax
*/
protected def And(description: String): Unit
}Usage Example:
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatest.GivenWhenThen
class ExampleFeatureSpec extends AnyFeatureSpec with GivenWhenThen {
feature("Stack operations") {
scenario("User pops an element from a non-empty stack") {
Given("a non-empty stack")
val stack = new Stack[String]
stack.push("item1")
stack.push("item2")
When("the user pops an element")
val poppedElement = stack.pop()
Then("the most recently pushed element should be returned")
assert(poppedElement === "item2")
And("the stack size should be reduced by one")
assert(stack.size === 1)
}
}
}trait TestRegistration {
def registerTest(testName: String, testTags: Tag*)(testFun: => Any): Unit
def registerIgnoredTest(testName: String, testTags: Tag*)(testFun: => Any): Unit
}
trait TestSuite extends Suite {
def testNames: Set[String]
def tags: Map[String, Set[String]]
def runTest(testName: String, args: Args): Status
}