Testing with MUnit
Prerequisites: Building with sbt and Scala CLI
MUnit is a simple, fast testing library, and the default in Scala CLI and the Scala 3 project template. (ScalaTest is the other widely used option, with several testing styles.)
// illustrative
// Pricing.scala
object Pricing:
def discount(total: BigDecimal): BigDecimal =
require(total >= 0, "total cannot be negative")
if total >= 100 then total * BigDecimal("0.1") else BigDecimal(0)
// illustrative
// Pricing.test.scala
//> using test.dep org.scalameta::munit:1.1.0
class PricingSuite extends munit.FunSuite:
test("no discount under 100") {
assertEquals(Pricing.discount(50), BigDecimal(0))
}
test("10% discount at 100 or more") {
assertEquals(Pricing.discount(200), BigDecimal(20))
}
test("negative totals are rejected") {
intercept[IllegalArgumentException](Pricing.discount(-1))
}
$ scala test .
PricingSuite:
+ no discount under 100 0.012s
+ 10% discount at 100 or more 0.001s
+ negative totals are rejected 0.002s
- A test suite is a class extending
munit.FunSuite; eachtestis one case. assertEquals(obtained, expected)shows a clear diff on failure;assert(condition),intercept[E], andassertNoDiff(for multi-line strings) cover most other needs.- In an sbt project, tests live in
src/test/scalaand run withsbt test.
Fixtures and property-based testing
// illustrative
class ListSuite extends munit.ScalaCheckSuite:
import org.scalacheck.Prop.forAll
property("reversing twice gives the original list") {
forAll { (xs: List[Int]) => xs.reverse.reverse == xs }
}
Property-based tests (with ScalaCheck) generate hundreds of random inputs and check that a property always holds — excellent at finding edge cases you didn't think of. Pure functions over immutable data are, happily, the easiest code to test this way.