sbt projects and testing with MUnit

Scala · Practical · 15 min

Real projects need a build, dependencies and tests; sbt is the most widely used Scala build tool.

sbt is the standard Scala build tool. A minimal project is a build.sbt plus project/build.properties (pinning the sbt version) and sources under src/main/scala and tests under src/test/scala. Start the sbt shell once with sbt and run tasks inside it, keeping the JVM warm: compile, run, test, testOnly *ParserSuite, console and ~test (rerun on file changes).

Dependencies use the form "org" %% "artifact" % "version". The double %% appends the Scala binary version (_3) to the artifact name, picking the build for your Scala version; a single % is for plain Java libraries. Test-only libraries get % Test. Multi-module builds define several projects with lazy val core = project.in(file("core")) and dependsOn.

MUnit is a lightweight testing library that works with sbt and Scala CLI. A test suite extends munit.FunSuite and declares test("name") { ... } blocks with assertEquals(obtained, expected), assert(cond) and intercept[Ex] { ... }. It prints readable diffs on failure. ScalaTest is the older, larger alternative, and ScalaCheck adds property-based tests.

For single-file work, Scala CLI handles tests too: files ending in .test.scala with //> using test.dep org.scalameta::munit::1.1.0 run with scala test ..

Example

// build.sbt
ThisBuild / scalaVersion := "3.7.1"

lazy val root = project
  .in(file("."))
  .settings(
    name := "calc",
    libraryDependencies ++= Seq(
      "com.lihaoyi"   %% "upickle" % "4.1.0",
      "org.scalameta" %% "munit"   % "1.1.0" % Test
    )
  )

// src/test/scala/CalcSuite.scala
class CalcSuite extends munit.FunSuite:
  def divide(a: Int, b: Int): Either[String, Int] =
    if b == 0 then Left("division by zero") else Right(a / b)

  test("divides evenly") {
    assertEquals(divide(10, 2), Right(5))
  }
  test("rejects zero") {
    assert(divide(1, 0).isLeft)
  }
// $ sbt test   -> + divides evenly, + rejects zero, Passed: Total 2

Remember

  • build.sbt declares settings; run tasks in the sbt shell
  • %% picks the artifact built for your Scala version; % Test scopes it
  • MUnit: extend FunSuite, write test(...) with assertEquals
  • ~test reruns tests on every save

Watch out for

  • Using a single % for a Scala library, which fails to resolve or pulls in the wrong binary version.

Check yourself

  1. What does %% do in "org.scalameta" %% "munit" % "1.1.0"?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.scala-sbt.org/1.x/docs/

Builds on

Unlocks

Nothing builds on this yet.