CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/scalatest

Configures and runs ScalaTest - Scala-native test framework with multiple specification styles (FlatSpec, FunSuite, WordSpec, FreeSpec, AsyncFlatSpec for async); Matchers DSL (`should equal`, `should contain`, `shouldBe a [Class]`); integrates with ScalaCheck for property-based testing; sbt + Maven + Gradle support; tagged-test selective execution. Use when working with Scala codebases.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
scalatest
description:
Configures and runs ScalaTest - Scala-native test framework with multiple specification styles (FlatSpec, FunSuite, WordSpec, FreeSpec, AsyncFlatSpec for async); Matchers DSL (`should equal`, `should contain`, `shouldBe a [Class]`); integrates with ScalaCheck for property-based testing; sbt + Maven + Gradle support; tagged-test selective execution. Use when working with Scala codebases.

scalatest

Overview

Per scalatest.org:

ScalaTest is the de facto Scala testing framework. Like Kotest, it offers multiple specification styles to match team preference.

For Java/Kotlin projects, junit5-tests or kotest-tests are more idiomatic. ScalaTest is the right pick for Scala-primary or Scala-only projects.

When to use

  • Scala project (Scala 2.13 or Scala 3).
  • Existing ScalaTest codebase.
  • Property-based testing alongside ScalaCheck (canonical pairing).

How to use

  1. Add scalatest (+ scalatestplus for ScalaCheck) to build.sbt as Test deps (Step 1).
  2. Pick one specification style (FlatSpec / FunSuite / WordSpec) and mix in Matchers (Step 2).
  3. Assert with the Matchers DSL - should equal, should contain, shouldBe a [Class] (references/matchers-dsl.md, Step 3).
  4. Return Future[Assertion] from AsyncFlatSpec bodies for async code (Step 4).
  5. Add forAll invariants via ScalaCheck and lifecycle hooks / loan-fixtures for setup (Steps 5-6).
  6. Tag slow / integration tests and select them with -n / -l (Step 7).
  7. Run sbt clean coverage test coverageReport in CI (Step 8).

Step 1 - Install

build.sbt:

libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % Test
libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test

Test files live under src/test/scala/.

Step 2 - Specification styles

Per scalatest.org/user_guide/selecting_a_style:

8+ styles. Common picks:

FlatSpec (BDD-style):

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class CalculatorSpec extends AnyFlatSpec with Matchers {
  "Calculator" should "add two numbers" in {
    Calculator.add(1, 2) should equal(3)
  }

  it should "throw on overflow" in {
    an [ArithmeticException] should be thrownBy {
      Calculator.add(Int.MaxValue, 1)
    }
  }
}

FunSuite (xUnit-style):

import org.scalatest.funsuite.AnyFunSuite

class CalculatorSuite extends AnyFunSuite {
  test("add two numbers") {
    assert(Calculator.add(1, 2) == 3)
  }
}

WordSpec (deeply-nested BDD):

import org.scalatest.wordspec.AnyWordSpec

class UserServiceSpec extends AnyWordSpec with Matchers {
  "A UserService" when {
    "creating a user" should {
      "set default role" in {
        UserService.create("alice").role shouldBe "user"
      }
    }
  }
}

Pick one style per project + stick with it.

Step 3 - Matchers DSL

Assert with the Matchers DSL - equality (should equal / shouldBe / shouldEqual), size + membership (have size / contain / contain only / contain inOrder), map keys/values, string ops, type checks (shouldBe a [Class]), and numeric ordering + float tolerance. Full example set in references/matchers-dsl.md.

Step 4 - Async tests

import org.scalatest.flatspec.AsyncFlatSpec
import scala.concurrent.Future

class AsyncSpec extends AsyncFlatSpec with Matchers {
  "fetchUser" should "return user data" in {
    fetchUser(1) map { user =>
      user.id shouldBe 1
    }
  }
}

AsyncFlatSpec test bodies return Future[Assertion] - ScalaTest handles the async lifecycle.

Step 5 - ScalaCheck integration

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
import org.scalacheck.Gen

class PropertyCheckSpec extends AnyFlatSpec with Matchers
                          with ScalaCheckPropertyChecks {
  "addition" should "be commutative" in {
    forAll { (a: Int, b: Int) =>
      a + b shouldBe b + a
    }
  }

  "concatenation length" should "be sum of lengths" in {
    forAll(Gen.alphaStr, Gen.alphaStr) { (a, b) =>
      (a + b).length shouldBe a.length + b.length
    }
  }
}

Cross-ref quickcheck-testing (in the qa-property-based plugin) for the property-based discipline (covers QuickCheck + ScalaCheck).

Step 6 - Lifecycle hooks

class WithFixturesSpec extends AnyFlatSpec with BeforeAndAfterAll
                         with BeforeAndAfter with Matchers {
  override def beforeAll(): Unit = {
    // once before all tests
  }

  override def afterAll(): Unit = {
    // once after all tests
  }

  before {
    // before each test
  }

  after {
    // after each test
  }
}

Or use the loan-fixture pattern (functional style):

def withDatabase(test: Database => Unit): Unit = {
  val db = createTestDb()
  try test(db)
  finally db.close()
}

"createUser" should "persist to db" in withDatabase { db =>
  val user = userService.create("alice", db)
  user.id should not be None
}

Step 7 - Tagged tests

import org.scalatest.Tag

object Slow extends Tag("Slow")
object Integration extends Tag("Integration")

class TaggedSpec extends AnyFlatSpec {
  "fast operation" should "work" in {
    // runs by default
  }

  "slow operation" should "work" taggedAs Slow in {
    // skipped unless -n Slow flag
  }
}

Selective run: sbt 'testOnly * -- -n Slow' or -l Slow to exclude Slow.

Step 8 - CI integration

- run: sbt clean coverage test coverageReport

Coverage via sbt-scoverage plugin (Scala-native; not JaCoCo).

Worked example

Testing Calculator.add with FlatSpec + ScalaCheck:

  1. Add "org.scalatest" %% "scalatest" % "3.2.19" % Test and "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % Test.
  2. Write class CalculatorSpec extends AnyFlatSpec with Matchers with "Calculator" should "add two numbers" in { Calculator.add(1, 2) should equal(3) }.
  3. Add an invariant via ScalaCheckPropertyChecks: forAll { (a: Int, b: Int) => a + b shouldBe b + a }.
  4. Run sbt test: the example passes; forAll shrinks to a minimal counterexample if commutativity breaks.
  5. Run sbt coverage test coverageReport for the scoverage HTML report.

Anti-patterns

Anti-patternWhy it failsFix
Mix specification styles in one projectReader confusionPick one (Step 2)
assert(x == y) instead of Matchers DSLLoses diff in failureUse x should equal(y) (Step 3)
Sync test bodies for async codeFuture never resolves; test passes wronglyUse AsyncFlatSpec (Step 4)
Skip ScalaCheck for invariantsMisses edge cases that fixed-input tests don't catchUse forAll (Step 5)

Limitations

  • Multiple styles is a feature with a flip side: bikeshedding + style mixing.
  • For Java/Kotlin teams, JUnit 5 / Kotest are more idiomatic.
  • Coverage tool (sbt-scoverage) is Scala-only; cross-language projects need JaCoCo separately.

References

  • st - ScalaTest landing
  • st-styles - selecting a style
  • references/matchers-dsl.md - Matchers DSL reference
  • scalacheck.org - ScalaCheck (property-based)
  • junit5-tests, kotest-tests, spock-tests, testng-tests - sister tools
  • quickcheck-testing - Haskell + Scala property-based
  • test-code-conventions
Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/scalatest badge