The Scala Toolkit

How to write tests?

Language

You can require the entire toolkit in a single line:

//> using toolkit latest

MUnit, being a testing framework, is only available in test files: files in a test directory or ones that have the .test.scala extension. Refer to the Scala CLI documentation to learn more about the test scope.

Alternatively, you can require just a specific version of MUnit:

//> using dep org.scalameta::munit:1.0.0-M7

In your build.sbt file, you can add the dependency on toolkit-test:

lazy val example = project.in(file("."))
  .settings(
    scalaVersion := "3.3.3",
    libraryDependencies += "org.scala-lang" %% "toolkit-test" % "0.1.7" % Test
  )

Here the Test configuration means that the dependency is only used by the source files in src/test.

Alternatively, you can require just a specific version of MUnit:

libraryDependencies += "org.scalameta" %% "munit" % "1.0.0-M7" % Test

In your build.sc file, you can add a test object extending Tests and TestModule.Munit:

object example extends ScalaModule {
  def scalaVersion = "3.3.3"
  object test extends Tests with TestModule.Munit {
    def ivyDeps =
      Agg(
        ivy"org.scala-lang::toolkit-test:0.1.7"
      )
  }
}

Alternatively, you can require just a specific version of MUnit:

ivy"org.scalameta::munit:1.0.0-M7"

Writing a test suite

A group of tests in a single class is called a test class or test suite.

Each test suite validates a particular component or feature of the software. Typically we define one test suite for each source file or class that we want to test.

In Scala CLI, the test file can live in the same folder as the actual code, but the name of the file must end with .test.scala. In the following, MyTests.test.scala is a test file.

example/
├── MyApp.scala
└── MyTests.test.scala

Other valid structures and conventions are described in the Scala CLI documentation.

In sbt, test sources go in the src/test/scala folder.

For instance, the following is the file structure of a project example:

example
└── src
    ├── main
    │   └── scala
    │       └── MyApp.scala
    └── test
        └── scala
            └── MyTests.scala

In Mill, test sources go in the test/src folder.

For instance, the following is the file structure of a module example:

example
└── src
|   └── MyApp.scala
└── test
    └── src
        └── MyTests.scala

In the test source file, define a suite containing a single test:

package example

class MyTests extends munit.FunSuite {
  test("sum of two integers") {
    val obtained = 2 + 2
    val expected = 4
    assertEquals(obtained, expected)
  }
}
package example

class MyTests extends munit.FunSuite:
  test("sum of two integers") {
    val obtained = 2 + 2
    val expected = 4
    assertEquals(obtained, expected)
  }

A test suite is a Scala class that extends munit.FunSuite. It contains one or more tests, each defined by a call to the test method.

In the previous example, we have a single test "sum of integers" that checks that 2 + 2 equals 4. We use the assertion method assertEquals to check that two values are equal. The test passes if all the assertions are correct and fails otherwise.

Assertions

It is important to use assertions in each and every test to describe what to check. The main assertion methods in MUnit are:

  • assertEquals to check that what you obtain is equal to what you expected
  • assert to check a boolean condition

The following is an example of a test that use assert to check a boolean condition on a list.

test("all even numbers") {
  val input: List[Int] = List(1, 2, 3, 4)
  val obtainedResults: List[Int] = input.map(_ * 2)
  // check that obtained values are all even numbers
  assert(obtainedResults.forall(x => x % 2 == 0))
}
test("all even numbers") {
  val input: List[Int] = List(1, 2, 3, 4)
  val obtainedResults: List[Int] = input.map(_ * 2)
  // check that obtained values are all even numbers
  assert(obtainedResults.forall(x => x % 2 == 0))
}

MUnit contains more assertion methods that you can discover in its documentation: assertNotEquals, assertNoDiff, fail, and compileErrors.

Contributors to this page: