MUnit

MUnit

  • Docs
  • Blog
  • GitHub

›All Blog Posts

All Blog Posts

  • Publish Scala 2 and Scala 3 macros together
  • Using ScalaCheck with MUnit
  • Cross-platform async testing support
  • MUnit is a new Scala testing library

Publish Scala 2 and Scala 3 macros together

January 5, 2021

Ólafur Páll Geirsson

Ólafur Páll Geirsson

The next release of MUnit makes use of a new compiler feature that allows you to publish Scala 2 and Scala 3 macros together in a single artifact. The blog post Forward Compatibility for the Scala 3 Transition by Jamie Thompson explains this feature in more detail. The Scala 3 Migration Guide contains a hands-on tutorial on how to use this feature. In this post, I want to share a small example to motivate why I think this feature will be critical for a smooth Scala 3 transition.

This blog post was written as part of a collaboration with the Scala Center.

While it's standard practice to cross-build a Scala library between 2.12 and 2.13, you should think twice before cross-building for Scala 2.13 and Scala 3. There is a chance you can skip 2.13 and publish only for Scala 3 instead. The reason you may want to skip 2.13 is to prevent unexpected runtime crashes.

To demonstrate how runtime crashes can happen, consider the following dependency graph for a Scala 3 application.

G app_3.0 app_3.0 a_library_3.0 a_library_3.0 app_3.0->a_library_3.0 b_library_2.13 b_library_2.13 app_3.0->b_library_2.13 munit_3.0 munit_3.0 a_library_3.0->munit_3.0 munit_2.13 munit_2.13 b_library_2.13->munit_2.13

The application has two direct dependencies (a_library_3.0, b_library_2.13) and one transitive dependency on MUnit. The problem is that the transitive MUnit dependency appears twice on the classpath: once for Scala 3 (munit_3.0) and once for Scala 2.13 (munit_2.13). If munit_3.0 and munit_2.13 have binary incompatibilities then the application may compile successfully but crash at runtime with a MethodNotFoundException or ClassNotFoundException.

The next release of MUnit avoids this problem by including Scala 2 macros in the munit_3.0 artifact. With this change, b_library_2.13 can depend on munit_3.0 and the dependency graph becomes like this instead.

G app_3.0 app_3.0 a_library_3.0 a_library_3.0 app_3.0->a_library_3.0 b_library_2.13 b_library_2.13 app_3.0->b_library_2.13 munit_3.0 munit_3.0 a_library_3.0->munit_3.0 b_library_2.13->munit_3.0

This change is possible thanks to the new -Ytasty-reader flag in the Scala 2.13.4 compiler that enables the Scala 2 compiler to read Scala 3 libraries.

This feature is new and has some limitations that's good to be aware of:

  • Scala 2.13.4 can only understand libraries that are published with the old Scala 3.0.0-M1 version (latest is 3.0.0-M3 at the time of this writing). The upcoming Scala 2.13.5 release will be able to understand newer Scala 3.x releases.
  • Some common features in Scala 2 macros don't work in Scala 3. Most notably, you can't use quasiquotes. In the case of MUnit, we had to replace typeOf[Location] with c.mirror.staticClass(classOf[Location].getName) because typeOf is itself implemented as a Scala 2 macro.
  • Your Scala 3 library needs a compile-time dependecy on scala-reflect:2.13.x,

Given these limitations, MUnit will continue to publish for 2.13. Nevertheless, it's a phenomenal achievement that it's at all possible to publish Scala 2 and Scala 3 macros together. For certain libraries, I'm optimistic this feature will be a critical component to smoothen the Scala 3 transition.

Using ScalaCheck with MUnit

March 24, 2020

Gabriele Petronella

Gabriele Petronella

Property-based testing is a popular testing style and its most widely used implementation for Scala is the ScalaCheck library.

Starting with version 0.7.0, MUnit introduces a dedicated integration for ScalaCheck, which we'll explore in this blog post.

Read More

Cross-platform async testing support

February 16, 2020

Ólafur Páll Geirsson

Ólafur Páll Geirsson

MUnit v0.4.x did not support truly async tests on platforms like JavaScript. For example, the following test case would succeed even if it returns a failing future:

test("should fail after 100 milliseconds") {
  val p = Promise[Unit]()
  setTimeout(100) {
    p.failure(new RuntimeException("boom"))
  }
  p.future
}

The latest MUnit v0.5.0 release fixes this issue by adding support for async tests on all platforms meaning that the example test case above fails as expected after 100 milliseconds.

Read More

MUnit is a new Scala testing library

February 1, 2020

Ólafur Páll Geirsson

Ólafur Páll Geirsson

Hello world! I'm excited to announce the first release of MUnit, a new Scala testing library with a focus on actionable errors and extensible APIs. You may be thinking "Why create Yet Another Scala testing library?". It's a good question and this post is my attempt to explain the motivations for creating MUnit.

Read More
MUnit
Overview
Getting started
Social
Copyright © 2022 Scalameta