Use whenever the task is to write, add, fix or review JVM unit tests in Java, Kotlin, Scala or Groovy: JUnit 6 (Jupiter) by default and JUnit 5 upgrades, plus Kotest, Spock, ScalaTest and TestNG. Covers parameterized tests, lifecycle hooks, Mockito, AssertJ, JaCoCo coverage and Maven or Gradle CI.
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Companion reference for jvm-unit-tests. Consult for Scala projects
(Scala 2.13 or 3) or existing ScalaTest codebases. For Java/Kotlin
projects, JUnit 5 (SKILL.md) or Kotest (kotest.md) are more
idiomatic.
Per scalatest.org:
ScalaTest is the de facto Scala testing framework, with multiple specification styles and a Matchers DSL; ScalaCheck is its canonical property-based pairing.
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/.
Per scalatest.org/user_guide/selecting_a_style - pick one style per project:
FlatSpec (BDD-style, the recommended default):
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): test("add two numbers") { assert(Calculator.add(1, 2) == 3) }.
WordSpec (deeply-nested BDD): "A UserService" when { "creating a user" should { "set default role" in { … } } }.
Per scalatest.org/user_guide/using_matchers:
result should equal(42)
result shouldBe 42 // strict equality (uses ==)
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")
string should startWith("hello")
string should fullyMatch regex("\\d+")
opt shouldBe defined
either shouldBe Right(42)
result should be > 10
result should be (within(1.0) of 42.0) // float toleranceAsyncFlatSpec test bodies return Future[Assertion] - ScalaTest handles
the async lifecycle:
class AsyncSpec extends AsyncFlatSpec with Matchers {
"fetchUser" should "return user data" in {
fetchUser(1) map { user => user.id shouldBe 1 }
}
}Sync test bodies for async code are a silent-pass trap - the Future never resolves inside the assertion.
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 }
}
}forAll shrinks to a minimal counterexample on failure
(scalacheck.org). For the property-based discipline see the
qa-property-based plugin.
BeforeAndAfterAll (beforeAll / afterAll) + BeforeAndAfter
(before { … } / after { … }), or the loan-fixture pattern:
def withDatabase(test: Database => Unit): Unit = {
val db = createTestDb()
try test(db)
finally db.close()
}
"createUser" should "persist to db" in withDatabase { db => ... }object Slow extends Tag("Slow")
"slow operation" should "work" taggedAs Slow in { ... }Selective run: sbt 'testOnly * -- -n Slow' (include) / -l Slow
(exclude).
- run: sbt clean coverage test coverageReportCoverage via the sbt-scoverage plugin (Scala-native, not JaCoCo);
cross-language projects need JaCoCo separately for the Java/Kotlin side.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mix specification styles in one project | Reader confusion | Pick one |
assert(x == y) instead of Matchers DSL | Loses diff on failure | x should equal(y) |
| Sync test bodies for async code | Future never resolves; false pass | AsyncFlatSpec |