MUnit Documentation

repository·main·Indexed 19 days ago

https://github.com/scalameta/munit

MUnit is a Scala testing library focused on providing actionable error messages and extensible APIs. It includes a variety of assertions such as assert(), assertEquals(), assertMatches(), and compileErrors(), as well as tools for filtering tests via tags, categories, or the .only and .ignore modifiers. The library supports flexible resource management through functional test-local fixtures (FunFixture), reusable Fixture[T] objects, and ad-hoc lifecycle methods for both test-local and suite-local setup and teardown.

Tokens
18.4K
Snippets
68
Records
77
Agent score
67%

What's inside MUnit

  1. Explore MUnit external integrations

    main

    MUnit supports a variety of third-party integrations for property testing, effect systems, contract testing, and infrastructure. Use these libraries to extend MUnit's capabilities for specific ecosystems like cats-effect, ZIO, or http4s.

    - [discipline-munit](https://github.com/typelevel/discipline-munit): MUnit binding for Typelevel Discipline
    - [http4s-munit](https://github.com/alejandrohdezma/http4s-munit): Integration library between MUnit and http4s
    - [munit-cats-effect](https://github.com/typelevel/munit-cats-effect): Integration library for MUnit & cats-effect
    - [munit-scalacheck](https://github.com/scalameta/munit-scalacheck): Scalacheck integration for MUnit
    - [munit-snapshot](https://github.com/lolgab/munit-snapshot): Snapshot testing for MUnit
    - [munit-zio](https://github.com/poslegm/munit-zio): MUnit and ZIO integration
    - [pact4s](https://github.com/jbwheatley/pact4s): Consumer driven contract testing
    - [sbt-scripted-munit](https://github.com/alejandrohdezma/sbt-scripted-munit): SBT plugin to enable using MUnit to test your SBT plugins
    - [scala-hedgehog](https://github.com/hedgehogqa/scala-hedgehog): Release with confidence, state-of-the-art property testing for Scala
    - [tapir-golden-openapi-munit](https://github.com/alejandrohdezma/tapir-golden-openapi-munit): Golden testing for Tapir endpoints using MUnit
    - [testcontainers-scala](https://github.com/testcontainers/testcontainers-scala): Docker containers for testing in scala
  2. Async testing support in MUnit v0.5.0+

    main

    Starting with MUnit v0.5.0, the library provides true cross-platform support for asynchronous tests. In previous versions (v0.4.x), async tests on platforms like JavaScript could incorrectly pass even if they returned a failing Future. In v0.5.0 and later, MUnit correctly tracks and waits for the completion of Futures, ensuring that failing futures trigger test failures as expected.

    test("should fail after 100 milliseconds") {
      val p = Promise[Unit]()
      setTimeout(100) {
        p.failure(new RuntimeException("boom"))
      }
      p.future
    }
  3. Filter tests using Tags and TestTransforms

    main

    MUnit allows you to enable or disable tests based on environmental conditions (like OS or Scala version) using Tags and TestTransforms.

    To implement custom filtering:

    1. Define a custom Tag (e.g., object Windows213 extends Tag("Windows213")).
    2. Override munitTestTransforms in your suite to provide a TestTransform.
    3. Inside the transform, check the test's tags and use test.tag(Ignore) to disable it if conditions aren't met.

    This prevents running invalid tests in incompatible environments.

    import scala.util.Properties
    import munit._
    
    object Windows213 extends Tag("Windows213")
    
    class MySuite extends FunSuite {
      override def munitTestTransforms = super.munitTestTransforms ++ List(
        new TestTransform("Windows213", {
          test =>
            val isIgnored =
              test.tags(Windows213) && !(
                Properties.isWin &&
                  Properties.versionNumberString.startsWith("2.13")
              )
            if (isIgnored) test.tag(Ignore)
            else test
        })
      )
    
      test("windows-213".tag(Windows213)) {
        // Only runs when operating system is Windows and Scala version is 2.13
      }
    
      test("normal test") {
        // Always runs like a normal test.
      }
    }
  4. Integrate MUnit with JUnit tooling

    main

    MUnit is implemented as a JUnit runner. This means any tool that supports JUnit can run MUnit test suites without custom configuration. Supported integrations include:

    • IntelliJ IDEA: Automatically detects and runs MUnit suites.
    • Build Tools: Gradle and Pants can run MUnit tests via their existing JUnit integrations.
  5. Migrate from ScalaTest to MUnit ScalaCheck

    main

    To migrate from ScalaTest's property-based testing to MUnit:

    1. From ScalaCheck style (Checkers trait): Replace FunSuite with ScalaCheckSuite, replace test with property, and replace check with forAll.
    2. From ScalaTest style (GeneratorDrivenPropertyChecks): Replace FunSuite with ScalaCheckSuite, replace test with property, and convert ScalaTest matchers to MUnit assertions (e.g., should be becomes assertEquals).
    import munit.ScalaCheckSuite
    import org.scalacheck.Prop._
    
    class IntegerSuite extends ScalaCheckSuite {
    
      property("addition and multiplication are commutative") {
        forAll { (n1: Int, n2: Int) =>
          assertEquals(n1 + n2, n2 + n1)
          assertEquals(n1 * n2, n2 * n1)
        }
      }
    
    }
  6. Port existing ScalaCheck `Properties` to MUnit

    main

    If you have existing properties defined using the ScalaCheck Properties class, you can port them to MUnit by creating a PropertiesAdapter trait that iterates through the properties and registers them using MUnit's property method.

    import org.scalacheck.Properties
    import munit.ScalaCheckSuite
    import org.scalacheck.Prop._
    
    trait PropertiesAdapter {
      self: ScalaCheckSuite =>
      def include(ps: Properties): Unit =
        for ((name, prop) <- ps.properties) property(name)(prop)
    }
    
    object LegacyIntProps extends Properties("Int") {
      property("commutative") = forAll((x: Int, y: Int) => x + y == y + x)
      property("identity") = forAll((x: Int) => x + 0 == x)
    }
    
    class IntSuite extends ScalaCheckSuite with PropertiesAdapter {
    
      include(LegacyIntProps)
    
      property("addition is associative") = forAll { (x: Int, y: Int, z: Int) =>
        x + y + z == x + (y + z)
      }
    
    }
  7. Use asynchronous fixtures with `FutureFixture`

    main

    For asynchronous setup and teardown, extend FutureFixture[T]. This allows your lifecycle methods (beforeAll, beforeEach, afterEach, and afterAll) to return Future[Unit] values.

    Note: This feature is only available in the latest unstable version.

    import java.nio.file._
    import munit.FutureFixture
    import munit.FunSuite
    import scala.concurrent.Future
    import scala.concurrent.ExecutionContext.Implicits.global
    
    class AsyncFilesSuite extends FunSuite {
      val file = new FutureFixture[Path]("files") {
        var file: Path = null
        def apply() = file
        override def beforeEach(context: BeforeEach): Future[Unit] = Future {
          file = Files.createTempFile("files", context.test.name)
        }
        override def afterEach(context: AfterEach): Future[Unit] = Future {
          Files.deleteIfExists(file)
        }
      }
    
      override def munitFixtures = List(file)
    
      test("exists") {
        assert(Files.exists(file()))
      }
    }
  8. Control test logging verbosity

    main

    You can control the amount of test output printed using the --log=LEVEL flag. This allows you to filter output from just errors to highly detailed trace information. This setting is independent of your logger configuration (e.g., --logger=sbt).

    Available levels:

    • --log=error: Prints failing tests only.
    • --log=warn: Prints failing tests and non-successful tests (like ignored or skipped tests).
    • --log=info (default): Prints failing tests, suite-finished events, and entire-run started/finished events.
    • --log=debug: Prints everything in info plus suite-started and test-succeeded events.
    • --log=trace: Prints everything in debug plus test-started events (equivalent to --verbose).
    $ sbt
    > myproject/testOnly -- --log=warn
  9. Install MUnit and set up a test suite

    main

    To use MUnit, add the dependency to your build.sbt and register the MUnit test framework.

    After setup, you can write tests by extending munit.FunSuite and using the test method with assertions like assert.

    Supported Scala versions and platforms:

    • Scala 2.11.x, 2.12.x, 2.13.x: JVM, Scala.js (0.6.x, 1.x)
    • Scala 0.21.x (Dotty): JVM
    • Scala 2.11.x: Native (0.4.x)
    // build.sbt
    libraryDependencies += "org.scalameta" %% "munit" % "0.4.3"
    testFrameworks += new TestFramework("munit.Framework")
    
    // src/test/scala/com/MySuite.scala
    class MySuite extends munit.FunSuite {
      test("hello") {
        assert(41 == 42)
      }
    }