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.3
In your build.sbt file, you can add the dependency on toolkit-test:
lazy val example = project.in(file("."))
.settings(
scalaVersion := "3.3.4",
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.3" % 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.4"
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.3"
FunFixture
In MUnit, we use functional fixtures to manage resources in a concise and safe way.
A FunFixture
creates one resource for each test, ensuring that each test runs in isolation from the others.
In a test suite, you can define and use a FunFixture
as follows:
class FileTests extends munit.FunSuite {
val usingTempFile: FunFixture[os.Path] = FunFixture(
setup = _ => os.temp(prefix = "file-tests"),
teardown = tempFile => os.remove(tempFile)
)
usingTempFile.test("overwrite on file") { tempFile =>
os.write.over(tempFile, "Hello, World!")
val obtained = os.read(tempFile)
assertEquals(obtained, "Hello, World!")
}
}
class FileTests extends munit.FunSuite:
val usingTempFile: FunFixture[os.Path] = FunFixture(
setup = _ => os.temp(prefix = "file-tests"),
teardown = tempFile => os.remove(tempFile)
)
usingTempFile.test("overwrite on file") { tempFile =>
os.write.over(tempFile, "Hello, World!")
val obtained = os.read(tempFile)
assertEquals(obtained, "Hello, World!")
}
usingTempFile
is a fixture of type FunFixture[os.Path]
.
It contains two functions:
- The
setup
function, of typeTestOptions => os.Path
, creates a new temporary file. - The
teardown
function, of typeos.Path => Unit
, deletes this temporary file.
We use the usingTempFile
fixture to define a test that needs a temporary file.
Notice that the body of the test takes a tempFile
, of type os.Path
, as parameter.
The fixture automatically creates this temporary file, calls its setup
function, and cleans it up after the test by calling teardown
.
In the example, we used a fixture to manage a temporary file. In general, fixtures can manage other kinds of resources, such as a temporary folder, a temporary table in a database, a connection to a local server, and so on.
Composing FunFixture
s
In some tests, you may need more than one resource.
You can use FunFixture.map2
to compose two functional fixtures into one.
val using2TempFiles: FunFixture[(os.Path, os.Path)] =
FunFixture.map2(usingTempFile, usingTempFile)
using2TempFiles.test("merge two files") {
(file1, file2) =>
// body of the test
}
val using2TempFiles: FunFixture[(os.Path, os.Path)] =
FunFixture.map2(usingTempFile, usingTempFile)
using2TempFiles.test("merge two files") {
(file1, file2) =>
// body of the test
}
Using FunFixture.map2
on a FunFixture[A]
and a FunFixture[B]
returns a FunFixture[(A, B)]
.
Other fixtures
FunFixture
is the recommended type of fixture because:
- it is explicit: each test declares the resource they need,
- it is safe to use: each test uses its own resource in isolation.
For more flexibility, MUnit
contains other types of fixtures: the reusable fixture, the ad-hoc fixture and the asynchronous fixture.
Learn more about them in the MUnit documentation.