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 scalatestscalatest
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
How to use
Step 1 - Install
build.sbt:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % Test
libraryDependencies += "org.scalatestplus" %% "scalacheck-1-17" % "3.2.18.0" % TestTest 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 coverageReportCoverage via sbt-scoverage plugin (Scala-native; not JaCoCo).
Worked example
Testing Calculator.add with FlatSpec + ScalaCheck:
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mix specification styles in one project | Reader confusion | Pick one (Step 2) |
assert(x == y) instead of Matchers DSL | Loses diff in failure | Use x should equal(y) (Step 3) |
| Sync test bodies for async code | Future never resolves; test passes wrongly | Use AsyncFlatSpec (Step 4) |
| Skip ScalaCheck for invariants | Misses edge cases that fixed-input tests don't catch | Use forAll (Step 5) |
Limitations
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 toleranceFor 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).