Writing assertions
MUnit provides a few ways to fail a test given a condition.
assert()
Use assert() to fail a test if a boolean condition does not hold true. For example, assume we have two values:
val a = 1
// a: Int = 1
val b = 2
// b: Int = 2
In the most basic case when no hints are provided, the error message is "assertion failed" when the condition is false.
assert(a > b)
// munit.FailException: assertion failed
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assert$1(Assertions.scala:31)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assert(Assertions.scala:28)
// at munit.Assertions.assert$(Assertions.scala:24)
// at munit.FunSuite.assert(FunSuite.scala:11)
// at repl.Session$App$$anonfun$3.apply$mcV$sp(assertions.md:29)
// at repl.Session$App$$anonfun$3.apply(assertions.md:29)
// at repl.Session$App$$anonfun$3.apply(assertions.md:29)
Include an optional message to explain why the assertion failed.
assert(a > b, "a was smaller than b")
// munit.FailException: a was smaller than b
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assert$1(Assertions.scala:31)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assert(Assertions.scala:28)
// at munit.Assertions.assert$(Assertions.scala:24)
// at munit.FunSuite.assert(FunSuite.scala:11)
// at repl.Session$App$$anonfun$4.apply$mcV$sp(assertions.md:37)
// at repl.Session$App$$anonfun$4.apply(assertions.md:37)
// at repl.Session$App$$anonfun$4.apply(assertions.md:37)
Use clue() to include optional clues in the boolean condition based on values in the expression.
assert(clue(a) > clue(b))
// munit.FailException: assertion failed
// Clues {
// a: Int = 1
// b: Int = 2
// }
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assert$1(Assertions.scala:31)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assert(Assertions.scala:28)
// at munit.Assertions.assert$(Assertions.scala:24)
// at munit.FunSuite.assert(FunSuite.scala:11)
// at repl.Session$App$$anonfun$5.apply$mcV$sp(assertions.md:45)
// at repl.Session$App$$anonfun$5.apply(assertions.md:45)
// at repl.Session$App$$anonfun$5.apply(assertions.md:45)
Note, the
-Yrangeposcompiler option is required forclue()to work correctly. When-Yrangeposis not enabled you may see output like this instead:// Clues { // : Int = 1 // : Int = 2 // }To fix this problem in sbt, add the following line to your settings:
// build.sbt lazy val myProject = project .settings( + scalacOptions += "-Yrangepos" )
Clues can wrap more complicated expressions.
assert(clue(List(a).head) > clue(b))
// munit.FailException: assertion failed
// Clues {
// List(a).head: Int = 1
// b: Int = 2
// }
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assert$1(Assertions.scala:31)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assert(Assertions.scala:28)
// at munit.Assertions.assert$(Assertions.scala:24)
// at munit.FunSuite.assert(FunSuite.scala:11)
// at repl.Session$App$$anonfun$6.apply$mcV$sp(assertions.md:53)
// at repl.Session$App$$anonfun$6.apply(assertions.md:53)
// at repl.Session$App$$anonfun$6.apply(assertions.md:53)
assertEquals()
Use assertEquals() to assert that two values are the same.
assertEquals(a, b)
// munit.FailException: values are not the same
// => Obtained
// 1
// => Diff (- obtained, + expected)
// -1
// +2
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assertEquals$4(Assertions.scala:104)
// at munit.internal.difflib.Diffs$.assertNoDiff(Diffs.scala:23)
// at munit.Assertions.$anonfun$assertEquals$1(Assertions.scala:101)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assertEquals(Assertions.scala:100)
// at munit.Assertions.assertEquals$(Assertions.scala:94)
// at munit.FunSuite.assertEquals(FunSuite.scala:11)
// at repl.Session$App$$anonfun$7.apply$mcV$sp(assertions.md:61)
// at repl.Session$App$$anonfun$7.apply(assertions.md:61)
// at repl.Session$App$$anonfun$7.apply(assertions.md:61)
The error message automatically produces a diff on assertion failure.
case class Library(name: String, awesome: Boolean, versions: Range = 0.to(1))
val munitLibrary = Library("MUnit", true)
// munitLibrary: Library = Library("MUnit", true, Range.Inclusive(0, 1))
val mdocLibrary = Library("MDoc", true)
// mdocLibrary: Library = Library("MDoc", true, Range.Inclusive(0, 1))
assertEquals(munitLibrary, mdocLibrary)
// munit.FailException: values are not the same
// => Obtained
// Library(
// "MUnit",
// true,
// Range.Inclusive(
// 0,
// 1
// )
// )
// => Diff (- obtained, + expected)
// Library(
// - "MUnit",
// + "MDoc",
// true,
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assertEquals$4(Assertions.scala:104)
// at munit.internal.difflib.Diffs$.assertNoDiff(Diffs.scala:23)
// at munit.Assertions.$anonfun$assertEquals$1(Assertions.scala:101)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assertEquals(Assertions.scala:100)
// at munit.Assertions.assertEquals$(Assertions.scala:94)
// at munit.FunSuite.assertEquals(FunSuite.scala:11)
// at repl.Session$App$$anonfun$10.apply$mcV$sp(assertions.md:81)
// at repl.Session$App$$anonfun$10.apply(assertions.md:81)
// at repl.Session$App$$anonfun$10.apply(assertions.md:81)
Diffs make it easy to track down differences even in large data structures.
assertEquals(
Map(1 -> List(1.to(3))),
Map(1 -> List(1.to(4)))
)
// munit.FailException: values are not the same
// => Obtained
// Map(
// 1 -> List(
// Range.Inclusive(
// 1,
// 2,
// 3
// )
// )
// )
// => Diff (- obtained, + expected)
// 2,
// - 3
// + 3,
// + 4
// )
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assertEquals$4(Assertions.scala:104)
// at munit.internal.difflib.Diffs$.assertNoDiff(Diffs.scala:23)
// at munit.Assertions.$anonfun$assertEquals$1(Assertions.scala:101)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assertEquals(Assertions.scala:100)
// at munit.Assertions.assertEquals$(Assertions.scala:94)
// at munit.FunSuite.assertEquals(FunSuite.scala:11)
// at repl.Session$App$$anonfun$11.apply$mcV$sp(assertions.md:89)
// at repl.Session$App$$anonfun$11.apply(assertions.md:89)
// at repl.Session$App$$anonfun$11.apply(assertions.md:89)
Comparing two values of different types is a compile error.
assertEquals(1, "")
// error: Cannot prove that String <:< Int.
// Error occurred in an application involving default arguments.
// assertEquals(1, "")
// ^^^^^^^^^^^^^^^^^^^
The "expected" value (second argument) must be a subtype of the "obtained" value (first argument).
assertEquals(Option(1), Some(1))
It's a compile error if you swap the order of the arguments.
assertEquals(Some(1), Option(1))
// error: Cannot prove that Option[Int] <:< Some[Int].
// Error occurred in an application involving default arguments.
// assertEquals(Some(1), Option(1))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use assertEquals[Any, Any] if you really want to compare two different types.
val right1: Either[String , Int] = Right(42)
// right1: Either[String, Int] = Right(42)
val right2: Either[List[String], Int] = Right(42)
// right2: Either[List[String], Int] = Right(42)
assertEquals[Any, Any](right1, right2)
assertNotEquals()
Use assertNotEqual() to assert that two values are not the same.
assertNotEquals(a, a)
// munit.FailException: values are the same
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assertNotEquals$1(Assertions.scala:70)
// at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assertNotEquals(Assertions.scala:69)
// at munit.Assertions.assertNotEquals$(Assertions.scala:63)
// at munit.FunSuite.assertNotEquals(FunSuite.scala:11)
// at repl.Session$App$$anonfun$20.apply$mcV$sp(assertions.md:133)
// at repl.Session$App$$anonfun$20.apply(assertions.md:133)
// at repl.Session$App$$anonfun$20.apply(assertions.md:133)
The assertion does not fail when both values are different.
// OK
assertNotEquals(a, b)
assertNoDiff()
Use assertNoDiff() to compare two multiline strings.
val obtainedString = "val x = 41\nval y = 43\nval z = 43"
// obtainedString: String = """val x = 41
// val y = 43
// val z = 43"""
val expectedString = "val x = 41\nval y = 42\nval z = 43"
// expectedString: String = """val x = 41
// val y = 42
// val z = 43"""
assertNoDiff(obtainedString, expectedString)
// munit.FailException: diff assertion failed
// => Obtained
// """|val x = 41
// |val y = 43
// |val z = 43
// |""".stripMargin
// => Diff (- obtained, + expected)
// val x = 41
// -val y = 43
// +val y = 42
// val z = 43
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.$anonfun$assertNoDiff$2(Assertions.scala:56)
// at munit.internal.difflib.Diffs$.assertNoDiff(Diffs.scala:23)
// at munit.Assertions.$anonfun$assertNoDiff$1(Assertions.scala:53)
// at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:23)
// at munit.internal.console.StackTraces$.dropInside(StackTraces.scala:10)
// at munit.Assertions.assertNoDiff(Assertions.scala:53)
// at munit.Assertions.assertNoDiff$(Assertions.scala:47)
// at munit.FunSuite.assertNoDiff(FunSuite.scala:11)
// at repl.Session$App$$anonfun$25.apply$mcV$sp(assertions.md:156)
// at repl.Session$App$$anonfun$25.apply(assertions.md:156)
// at repl.Session$App$$anonfun$25.apply(assertions.md:156)
The difference between assertNoDiff() and assertEquals() is that assertEquals() fails according to the == method while assertNoDiff() ignores non-visible differences such as trailing/leading whitespace, Windows/Unix newlines and ANSI color codes. The "=> Obtained" section of assertNoDiff() error messages also include copy-paste friendly syntax using .stripMargin.
intercept()
Use intercept() when you expect a particular exception to be thrown by the test code (i.e. the test succeeds if the given exception is thrown).
intercept[java.lang.IllegalArgumentException]{
// code expected to throw exception here
}
// munit.FailException: expected exception of type 'java.lang.IllegalArgumentException' but body evaluated successfully
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.runIntercept(Assertions.scala:141)
// at munit.Assertions.intercept(Assertions.scala:126)
// at munit.Assertions.intercept$(Assertions.scala:123)
// at munit.FunSuite.intercept(FunSuite.scala:11)
// at repl.Session$App$$anonfun$26.apply(assertions.md:164)
// at repl.Session$App$$anonfun$26.apply(assertions.md:164)
interceptMessage()
Like intercept() except additionally asserts that the thrown exception has a specific error message.
interceptMessage[java.lang.IllegalArgumentException]("argument type mismatch"){
// code expected to throw exception here
}
// munit.FailException: expected exception of type 'java.lang.IllegalArgumentException' but body evaluated successfully
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at munit.Assertions.runIntercept(Assertions.scala:141)
// at munit.Assertions.interceptMessage(Assertions.scala:132)
// at munit.Assertions.interceptMessage$(Assertions.scala:129)
// at munit.FunSuite.interceptMessage(FunSuite.scala:11)
// at repl.Session$App$$anonfun$27.apply(assertions.md:174)
// at repl.Session$App$$anonfun$27.apply(assertions.md:174)
fail()
Use fail() to make the test case fail immediately.
fail("test failed")
// munit.FailException: test failed
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at repl.Session$App$$anonfun$28.apply(assertions.md:184)
// at repl.Session$App$$anonfun$28.apply(assertions.md:184)
Use clues() to include optional context why the test failed.
fail("test failed", clues(a + b))
// munit.FailException: test failed
// Clues {
// a + b: Int = 3
// }
// at munit.Assertions.fail(Assertions.scala:189)
// at munit.Assertions.fail$(Assertions.scala:183)
// at munit.FunSuite.fail(FunSuite.scala:11)
// at repl.Session$App$$anonfun$29.apply(assertions.md:192)
// at repl.Session$App$$anonfun$29.apply(assertions.md:192)
compileErrors()
Use compileErrors() to assert that an example code snippet fails with a specific compile-time error message.
assertNoDiff(
compileErrors("Set(2, 1).sorted"),
"""|error: value sorted is not a member of scala.collection.immutable.Set[Int]
|Set(2, 1).sorted
| ^
|""".stripMargin
)
The argument to compileErrors must be a string literal. It's not possible to pass in more complicated expressions such as variables or string interpolators.
val code = """val x: String = 2"""
compileErrors(code)
compileErrors(s"/* code */ $code")
// error: cannot compile dynamic expressions, only constant literals.
// To fix this problem, pass in a string literal in double quotes "..."
// compileErrors(code)
// ^^^^
// error: cannot compile dynamic expressions, only constant literals.
// To fix this problem, pass in a string literal in double quotes "..."
// compileErrors(s"/* code */ $code")
// ^^^^^^^^^^^^^^^^^^
Inline the code variable to fix the compile error.
compileErrors("val x: String = 2")
// res7: String = """error:
// type mismatch;
// found : Int(2)
// required: String
// val x: String = 2
// ^"""
