Testland
Browse all skills & agents

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.

Install with skills.sh (any agent)

npx skills add testland/qa --skill scalatest
View source

scalatest

Overview

Per scalatest.org (opens in new window):

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 (opens in new window):

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

ScalaTest - Matchers DSL

View source (opens in new window)

ScalaTest - Matchers DSL

Per scalatest.org/user_guide/using_matchers (opens in new window):

result should equal(42)
result shouldBe 42                    // strict equality (uses ==)
result shouldEqual 42                  // similar to equal but no parens
result should not equal 0
list should have size 5
list should contain("alice")
list should contain only("alice", "bob")
list should contain inOrder("alice", "bob")
map should contain key("alice")
map should contain value(42)
string should startWith("hello")
string should fullyMatch regex("\\d+")
opt shouldBe defined
opt shouldBe a [Some[_]]
result shouldBe a [Right[_, _]]
either shouldBe Right(42)
result should be > 10
result should be (within(1.0) of 42.0)   // float tolerance

For full Matchers reference, see st-matchers (opens in new window).

Related skills

assertj

Reference for AssertJ - the canonical JVM fluent-assertion library pairable with JUnit 5 / TestNG / Spock; covers the assertThat() entry point, collection matchers (contains, containsExactly, allSatisfy, extracting), exception assertions (assertThatThrownBy, catchThrowable), SoftAssertions for multi-failure collection, recursive comparison (usingRecursiveComparison) for deep equality, and domain-specific custom assertions via AbstractAssert. Use when writing JVM tests that need richer failure messages than built-in assertEquals, or when verifying complex object graphs, exception types, or collections.

junit5-tests

Configures and runs JUnit 5 (Jupiter) - modern JVM testing platform with annotations (`@Test` / `@ParameterizedTest` / `@RepeatedTest` / `@TestFactory`), lifecycle hooks (`@BeforeAll` / `@BeforeEach` / `@AfterEach` / `@AfterAll`), extension model (`@ExtendWith`), display names (`@DisplayName`), conditional execution (`@EnabledOnOs`, `@EnabledIf`), parallel execution config; integrates with Maven Surefire / Gradle test task / IntelliJ. Use when the user works with Java / Kotlin codebases needing the modern JVM standard.

kotest-tests

Configures and runs Kotest - Kotlin-native test framework with multiple specification styles (StringSpec, FunSpec, BehaviorSpec, DescribeSpec, ShouldSpec, FreeSpec, FeatureSpec, ExpectSpec, AnnotationSpec); rich matcher library; built-in property-based testing (alternative to jqwik); coroutines support; data-driven testing; isolation modes per-spec or per-test; integrates with Gradle JVM test task. Use when working with Kotlin and wanting Kotlin-idiomatic DSL over JUnit 5's annotation-driven approach. Matchers (shouldBe, shouldContain) are bundled with the runner and are not a drop-in replacement for a standalone JVM assertion library; for assertion-only use paired with JUnit 5 / TestNG / Spock see assertj.

spock-tests

Configures and runs Spock - Groovy-based JVM testing framework with given/when/then BDD blocks, where: data tables for parametrized tests, built-in mocking via Mock()/Stub()/Spy(), interaction-based testing (verify method calls in declarative DSL), implicit assertions in then: blocks. Use when working with Java/Kotlin codebases that benefit from Groovy DSL expressiveness, or maintaining existing Spock projects.

testng-tests

Configures and runs TestNG - JVM testing framework with `@Test` priorities + groups + `dependsOnMethods`; `@DataProvider` for parametrized tests with method-level data sources; `testng.xml` suite definitions for grouping + parallelism config; listeners (`ITestListener`, `ISuiteListener`) for hooks; `ITestContext` for cross-test state; integrates with Maven Surefire / Gradle. Use when working with legacy TestNG codebases or needing TestNG-specific features (test method dependencies, suite-level XML config).