scala-testing

Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.

8 stars

Best use case

scala-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.

Teams using scala-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/scala-testing/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/scala-testing/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/scala-testing/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How scala-testing Compares

Feature / Agentscala-testingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Scala Testing

## When to Activate

- Writing tests for Scala code
- Choosing a Scala test framework
- Testing Cats Effect or ZIO code
- Setting up integration tests with real databases
- Adding property-based tests with ScalaCheck to cover edge cases automatically
- Configuring Testcontainers for a PostgreSQL or other database in CI
- Reviewing test coverage and deciding between unit fakes and Mockito mocks

---

## ScalaTest

Standard, feature-rich framework. Prefer `AnyFunSpec` or `AnyFlatSpec`.

### AnyFunSpec — BDD-style

```scala
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers

class UserServiceSpec extends AnyFunSpec with Matchers:
  describe("UserService"):
    describe("find"):
      it("returns Some when user exists"):
        val repo = InMemoryUserRepo(Map(userId -> user))
        val svc  = UserService(repo)
        svc.find(userId) shouldBe Some(user)

      it("returns None when user not found"):
        val svc = UserService(InMemoryUserRepo())
        svc.find(userId) shouldBe None
```

### AnyFlatSpec — simpler style

```scala
class UserServiceSpec extends AnyFlatSpec with Matchers:
  "UserService.find" should "return Some for existing user" in {
    UserService(repo).find(userId) shouldBe Some(user)
  }

  it should "return None for missing user" in {
    UserService(InMemoryUserRepo()).find(userId) shouldBe None
  }
```

### Matchers Reference

```scala
result shouldBe Some(user)           // equality
result should not be empty
result shouldEqual List(1, 2, 3)
name should startWith("Alice")
list should have length 3
n should be > 0
thrown.getMessage should include("not found")

// Exception testing
an [IllegalArgumentException] should be thrownBy {
  validateAge(-1)
}
```

---

## MUnit — Lightweight and Fast

Recommended for new projects. Simpler setup, compatible with Cats Effect.

```scala
import munit.FunSuite

class UserSuite extends FunSuite:
  test("find returns Some for existing user"):
    val svc = UserService(InMemoryUserRepo(Map(userId -> user)))
    assertEquals(svc.find(userId), Some(user))

  test("find returns None for missing user"):
    assertEquals(
      UserService(InMemoryUserRepo()).find(userId),
      None
    )
```

---

## Property-Based Testing — ScalaCheck

Test invariants across random inputs:

```scala
import org.scalacheck.{Gen, Prop}
import org.scalacheck.Prop.forAll

object EmailSpec extends org.scalacheck.Properties("Email"):
  val validEmail: Gen[String] =
    for
      user   <- Gen.alphaStr.suchThat(_.nonEmpty)
      domain <- Gen.alphaStr.suchThat(_.nonEmpty)
    yield s"$user@$domain.com"

  property("encode then decode roundtrip") = forAll { (s: String) =>
    Email.parse(Email.format(s)) == s
  }

  property("valid emails always parse") = forAll(validEmail) { email =>
    Email.parse(email).isRight
  }
```

### MUnit + ScalaCheck

```scala
import munit.ScalaCheckSuite
import org.scalacheck.Prop.*

class SlugSuite extends ScalaCheckSuite:
  property("slug is always lowercase"):
    forAll { (s: String) =>
      val slug = s.toSlug
      slug == slug.toLowerCase
    }
```

---

## Cats Effect Testing — munit-cats-effect

```scala
import munit.CatsEffectSuite
import cats.effect.IO

class UserRepoSuite extends CatsEffectSuite:
  test("save and retrieve user"):
    for
      repo   <- IO(InMemoryUserRepo())
      _      <- repo.save(user)
      result <- repo.find(user.id)
    yield assertEquals(result, Some(user))

  test("concurrent saves are safe"):
    for
      repo <- IO(InMemoryUserRepo())
      _    <- (1 to 100).toList.parTraverse(i => repo.save(user.copy(id = UserId(i.toString))))
      all  <- repo.findAll
    yield assertEquals(all.size, 100)
```

### Resource Fixtures

```scala
val dbRepo: Fixture[UserRepo] =
  ResourceSuiteLocalFixture("db-repo",
    Resource.make(IO(PostgresUserRepo(testPool)))(r => IO(r.close()))
  )

override def munitFixtures = List(dbRepo)

test("saves to Postgres"):
  dbRepo().save(user) >> dbRepo().find(user.id).map:
    result => assertEquals(result, Some(user))
```

---

## ZIO Test

```scala
import zio.test.*
import zio.test.Assertion.*

object UserServiceSpec extends ZIOSpecDefault:
  def spec = suite("UserService")(
    test("find returns user when exists"):
      for
        svc    <- ZIO.service[UserService]
        result <- svc.find(userId)
      yield assert(result)(isSome(equalTo(user)))
  ).provide(
    UserService.live,
    InMemoryUserRepo.layer
  )
```

---

## Mocking with MockitoSugar

Prefer **fakes** (in-memory implementations) over mocks when possible.

When mocking is necessary:

```scala
import org.mockito.MockitoSugar
import org.mockito.ArgumentMatchers.any

class PaymentServiceSpec extends AnyFunSpec with MockitoSugar:
  val gateway = mock[PaymentGateway]

  describe("charge"):
    it("calls gateway with correct amount"):
      when(gateway.charge(any[Amount])) thenReturn IO.pure(PaymentResult.Success("tx-1", amount))

      val svc = PaymentService(gateway)
      svc.charge(amount).unsafeRunSync()

      verify(gateway).charge(amount)
```

---

## Testcontainers — Integration Tests

```scala
import com.dimafeng.testcontainers.{PostgreSQLContainer, ForAllTestContainer}

class PostgresRepoSpec extends AnyFunSpec with ForAllTestContainer:
  override val container = PostgreSQLContainer("postgres:16")

  lazy val repo = PostgresUserRepo(
    jdbcUrl  = container.jdbcUrl,
    username = container.username,
    password = container.password
  )

  describe("PostgresUserRepo"):
    it("persists and retrieves user"):
      repo.save(user)
      repo.find(user.id) shouldBe Some(user)
```

### MUnit + Testcontainers

```scala
class DbSuite extends CatsEffectSuite:
  val pg = ResourceSuiteLocalFixture("postgres",
    Resource.fromAutoCloseable(IO(PostgreSQLContainer("postgres:16").tap(_.start())))
  )

  override def munitFixtures = List(pg)

  test("saves to real postgres"):
    val repo = PostgresUserRepo.fromContainer(pg())
    repo.save(user) >> repo.find(user.id).map(assertEquals(_, Some(user)))
```

---

## Test Organization

```
src/
  test/
    scala/
      com/example/
        UserServiceSpec.scala       # Unit — pure, no I/O
        UserRepoSpec.scala          # Integration — Testcontainers
        PaymentServiceProperties.scala  # ScalaCheck properties
```

## Build Configuration

```scala
// build.sbt
libraryDependencies ++= Seq(
  "org.scalameta"     %% "munit"                  % "1.0.0"  % Test,
  "org.typelevel"     %% "munit-cats-effect"       % "2.0.0"  % Test,
  "org.scalatest"     %% "scalatest"               % "3.2.18" % Test,
  "org.scalacheck"    %% "scalacheck"              % "1.17.0" % Test,
  "org.mockito"       %% "mockito-scala"           % "1.17.31"% Test,
  "com.dimafeng"      %% "testcontainers-scala-postgresql" % "0.41.4" % Test
)
```

## Running Tests

```bash
sbt test                            # all tests
sbt "testOnly *UserServiceSpec"     # single spec
sbt "testOnly -- -z 'finds user'"  # by test name pattern
sbt "Test/testOptions += Tests.Argument(\"-v\")"  # verbose output
```

Related Skills

visual-testing

8
from marvinrichter/clarc

Visual Regression Testing: tool comparison (Chromatic/Percy/Playwright screenshots/BackstopJS), pixel-diff vs AI-based comparison, baseline management, flakiness strategies (masks, tolerances, waitForLoadState), CI integration with GitHub Actions, and Storybook integration.

typescript-testing

8
from marvinrichter/clarc

TypeScript testing patterns: Vitest for unit/integration, Playwright for E2E, MSW for API mocking, Testing Library for React components. Core TDD methodology for TypeScript/JavaScript projects.

swift-testing

8
from marvinrichter/clarc

Swift testing patterns: Swift Testing framework (Swift 6+), XCTest for UI tests, async/await test cases, actor testing, Combine testing, and XCUITest for UI automation. TDD for Swift/SwiftUI.

swift-protocol-di-testing

8
from marvinrichter/clarc

Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.

scala-patterns

8
from marvinrichter/clarc

Idiomatic Scala patterns: ADTs with sealed traits and case classes, typeclass pattern, Option/Either/Try error handling, for-comprehensions, Cats Effect (IO, Resource, Ref, Fiber), ZIO fundamentals, Scala 3 features (given/using, enums, extension methods, opaque types, union types). Covers both Scala 2.13 and Scala 3. Use when writing Scala, reviewing Scala code, or designing Scala domain models.

rust-testing

8
from marvinrichter/clarc

Rust testing patterns — unit tests with mockall, integration tests with sqlx transactions, HTTP handler testing (axum), benchmarks (criterion), property tests (proptest), fuzzing, and CI with cargo-nextest.

rust-testing-advanced

8
from marvinrichter/clarc

Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with

ruby-testing

8
from marvinrichter/clarc

RSpec testing patterns for Ruby and Rails — factories, mocks, request specs, feature specs, VCR, and SimpleCov coverage.

r-testing

8
from marvinrichter/clarc

R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.

python-testing

8
from marvinrichter/clarc

Python testing strategies using pytest, TDD methodology, fixtures, mocking, and parametrization. Core testing fundamentals.

python-testing-advanced

8
from marvinrichter/clarc

Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.

php-testing

8
from marvinrichter/clarc

PHP testing patterns: PHPUnit 11 with mocks and data providers, Pest v3 with expectations and datasets, Laravel feature/HTTP tests with RefreshDatabase, Symfony WebTestCase, PHPStan static analysis, Infection mutation testing. Use when writing or reviewing PHP tests.