ScalaTest is a comprehensive testing framework for Scala and Java that provides multiple testing styles and sophisticated matcher libraries.
—
ScalaTest provides eight different testing styles to accommodate various preferences and use cases. Each style offers a different syntax and organization approach while sharing the same underlying framework capabilities.
Function-based testing where each test is defined as a function with a descriptive name.
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.Tag
class MyFunSuite extends AnyFunSuite {
protected def test(testName: String, testTags: Tag*)(testFun: => Any): Unit
protected def ignore(testName: String, testTags: Tag*)(testFun: => Any): Unit
}
// Async version
import org.scalatest.funsuite.AsyncFunSuite
import scala.concurrent.Future
class MyAsyncFunSuite extends AsyncFunSuite {
protected def test(testName: String, testTags: Tag*)(testFun: => Future[compatible.Assertion]): Unit
}
// Fixture versions
import org.scalatest.funsuite.FixtureAnyFunSuite
class MyFixtureFunSuite extends FixtureAnyFunSuite {
type FixtureParam = MyFixture
def withFixture(test: OneArgTest): Outcome
}Usage Example:
import org.scalatest.funsuite.AnyFunSuite
class CalculatorSuite extends AnyFunSuite {
test("addition should work") {
assert(2 + 2 === 4)
}
test("subtraction should work") {
assert(4 - 2 === 2)
}
ignore("temporarily disabled test") {
// This test will be ignored
assert(1 === 2)
}
}BDD-style flat specification with "should" or "must" syntax.
import org.scalatest.flatspec.AnyFlatSpec
class MyFlatSpec extends AnyFlatSpec {
behavior of "subject"
"it" should "behavior description" in { /* test code */ }
"they" should "behavior description" in { /* test code */ }
ignore should "ignored behavior" in { /* test code */ }
}
// Async version
import org.scalatest.flatspec.AsyncFlatSpec
class MyAsyncFlatSpec extends AsyncFlatSpec {
"it" should "behavior description" in {
Future { assert(true) }
}
}Usage Example:
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class StackSpec extends AnyFlatSpec with Matchers {
behavior of "A Stack"
it should "pop values in last-in-first-out order" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
stack.pop() should be(2)
stack.pop() should be(1)
}
it should "throw NoSuchElementException if an empty stack is popped" in {
val emptyStack = new Stack[Int]
a [NoSuchElementException] should be thrownBy {
emptyStack.pop()
}
}
}Nested specification style using "when" and "should" for organization.
import org.scalatest.wordspec.AnyWordSpec
class MyWordSpec extends AnyWordSpec {
"subject" when {
"condition" should {
"behavior" in { /* test code */ }
}
}
"subject" should {
"behavior" in { /* test code */ }
}
}
// Async version
import org.scalatest.wordspec.AsyncWordSpec
class MyAsyncWordSpec extends AsyncWordSpec {
"subject" should {
"behavior" in {
Future { assert(true) }
}
}
}Usage Example:
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.matchers.should.Matchers
class StackSpec extends AnyWordSpec with Matchers {
"A Stack" when {
"empty" should {
"be empty" in {
val stack = new Stack[Int]
stack.isEmpty should be(true)
}
"throw NoSuchElementException when popped" in {
val stack = new Stack[Int]
a [NoSuchElementException] should be thrownBy {
stack.pop()
}
}
}
"non-empty" should {
"return the correct size" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
stack.size should be(2)
}
}
}
}Free-form specification style using dash (-) syntax for nesting.
import org.scalatest.freespec.AnyFreeSpec
class MyFreeSpec extends AnyFreeSpec {
"description" - {
"nested description" - {
"test description" in { /* test code */ }
}
"another test" in { /* test code */ }
}
}
// Async version
import org.scalatest.freespec.AsyncFreeSpec
class MyAsyncFreeSpec extends AsyncFreeSpec {
"description" - {
"test" in {
Future { assert(true) }
}
}
}
// Path-dependent version (creates new instance for each test)
import org.scalatest.freespec.PathAnyFreeSpec
class MyPathFreeSpec extends PathAnyFreeSpec {
"subject" - {
"when condition" - {
"should behavior" in {
// Each test gets a fresh instance
assert(true)
}
}
}
}Usage Example:
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers
class StackSpec extends AnyFreeSpec with Matchers {
"A Stack" - {
"when empty" - {
"should be empty" in {
val stack = new Stack[Int]
stack.isEmpty should be(true)
}
"should throw exception when popped" in {
val stack = new Stack[Int]
a [NoSuchElementException] should be thrownBy {
stack.pop()
}
}
}
"when containing elements" - {
"should not be empty" in {
val stack = new Stack[Int]
stack.push(1)
stack.isEmpty should be(false)
}
}
}
}BDD-style feature specification with "feature" and "scenario" keywords.
import org.scalatest.featurespec.AnyFeatureSpec
class MyFeatureSpec extends AnyFeatureSpec {
feature("feature description") {
scenario("scenario description") { /* test code */ }
ignore("ignored scenario") { /* test code */ }
}
}
// Async version
import org.scalatest.featurespec.AsyncFeatureSpec
class MyAsyncFeatureSpec extends AsyncFeatureSpec {
feature("feature") {
scenario("scenario") {
Future { assert(true) }
}
}
}Usage Example:
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatest.matchers.should.Matchers
class CalculatorSpec extends AnyFeatureSpec with Matchers {
feature("Calculator arithmetic operations") {
scenario("User adds two positive numbers") {
val calculator = new Calculator
val result = calculator.add(2, 3)
result should be(5)
}
scenario("User divides by zero") {
val calculator = new Calculator
a [ArithmeticException] should be thrownBy {
calculator.divide(10, 0)
}
}
}
feature("Calculator memory functions") {
scenario("User stores and recalls a value") {
val calculator = new Calculator
calculator.store(42)
calculator.recall() should be(42)
}
}
}RSpec-style specification with "describe" and "it" blocks.
import org.scalatest.funspec.AnyFunSpec
class MyFunSpec extends AnyFunSpec {
describe("subject") {
it("behavior description") { /* test code */ }
ignore("ignored behavior") { /* test code */ }
describe("nested subject") {
it("nested behavior") { /* test code */ }
}
}
}
// Async version
import org.scalatest.funspec.AsyncFunSpec
class MyAsyncFunSpec extends AsyncFunSpec {
describe("subject") {
it("behavior") {
Future { assert(true) }
}
}
}
// Path-dependent version (creates new instance for each test)
import org.scalatest.funspec.PathAnyFunSpec
class MyPathFunSpec extends PathAnyFunSpec {
describe("subject") {
it("behavior description") {
// Each test gets a fresh instance
assert(true)
}
describe("nested subject") {
it("nested behavior") {
assert(true)
}
}
}
}Usage Example:
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers
class StackSpec extends AnyFunSpec with Matchers {
describe("A Stack") {
describe("when empty") {
it("should be empty") {
val stack = new Stack[Int]
stack.isEmpty should be(true)
}
it("should throw exception when popped") {
val stack = new Stack[Int]
a [NoSuchElementException] should be thrownBy {
stack.pop()
}
}
}
describe("when it has one item") {
it("should have size 1") {
val stack = new Stack[Int]
stack.push(9)
stack.size should be(1)
}
}
}
}Property-based testing style for defining properties that should hold for ranges of data.
import org.scalatest.propspec.AnyPropSpec
class MyPropSpec extends AnyPropSpec {
property("property description") { /* test code */ }
ignore("ignored property") { /* test code */ }
}
// Async version
import org.scalatest.propspec.AsyncPropSpec
class MyAsyncPropSpec extends AsyncPropSpec {
property("property") {
Future { assert(true) }
}
}Usage Example:
import org.scalatest.propspec.AnyPropSpec
import org.scalatest.matchers.should.Matchers
class StringSpec extends AnyPropSpec with Matchers {
property("string concatenation is associative") {
val a = "hello"
val b = " "
val c = "world"
(a + b) + c should equal(a + (b + c))
}
property("string length is additive for concatenation") {
val s1 = "hello"
val s2 = "world"
val combined = s1 + s2
combined.length should equal(s1.length + s2.length)
}
}Reflection-based specification style where test methods are identified by naming convention.
import org.scalatest.refspec.RefSpec
class MyRefSpec extends RefSpec {
// Methods starting with "test" are test methods
def `test: description`(): Unit = { /* test code */ }
def testDescription(): Unit = { /* test code */ }
// Methods starting with "ignore" are ignored
def `ignore: description`(): Unit = { /* test code */ }
}Usage Example:
import org.scalatest.refspec.RefSpec
import org.scalatest.matchers.should.Matchers
class StackRefSpec extends RefSpec with Matchers {
def `test: empty stack should be empty`(): Unit = {
val stack = new Stack[Int]
stack.isEmpty should be(true)
}
def `test: stack should pop in LIFO order`(): Unit = {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
stack.pop() should be(2)
stack.pop() should be(1)
}
def `ignore: temporarily disabled test`(): Unit = {
// This test will be ignored
assert(false)
}
}All test styles support fixture variants for managing test resources:
// Fixture variants exist for all styles
import org.scalatest.funsuite.FixtureAnyFunSuite
import org.scalatest.flatspec.FixtureAnyFlatSpec
import org.scalatest.wordspec.FixtureAnyWordSpec
// ... and so on
abstract class FixtureTestSuite extends FixtureAnyFunSuite {
type FixtureParam = YourFixtureType
def withFixture(test: OneArgTest): Outcome = {
// Setup fixture
val fixture = createFixture()
try {
test(fixture)
} finally {
// Cleanup fixture
cleanupFixture(fixture)
}
}
}All styles can be mixed in a single test suite and share the same assertion and matcher capabilities.
FreeSpec and FunSpec support path-dependent variants (PathAnyFreeSpec, PathAnyFunSpec) that create a new instance of the test class for each test. This provides:
Use path-dependent styles when you need strong test isolation or have complex shared setup requirements.
Install with Tessl CLI
npx tessl i tessl/maven-org-scalatest--scalatest-2-11