1. Introduction
In this tutorial, we’ll focus on using JUnit in Scala. We’re going to start with JUnit 4 and then take a look at using JUnit 5.
We’ll also see how JUnit works with ScalaTest.
2. JUnit 4
2.1. Dependencies
Let’s start by adding the junit-interface to our dependencies. This will import JUnit as a transient dependency:
libraryDependencies ++= "com.github.sbt" % "junit-interface" % "0.13.3" % "test"
The junit-interface allows running JUnit tests from SBT.
2.2. Our First Tests
We now have everything necessary to start writing our first test:
package com.baeldung.scala.junit4
import org.junit.Test
import org.junit.Assert._
class IntJunitTests {
@Test
def testOneIsPositive {
assertTrue(1 > 0)
}
@Test
def testMinusOneIsNegative {
assertTrue(-1 < 0)
}
}
Not very interesting tests, but we can already learn something from them. Let’s run the tests from the sbt shell using the test command:
sbt:scala-test-junit4> test
[debug] Test run started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative finished, took 0.0 sec
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive finished, took 0.0 sec
[debug] Test run finished: 0 failed, 0 ignored, 2 total, 0.005s
[debug] Summary for JUnit not available.
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2
[debug] Passed tests:
[debug] com.baeldung.scala.junit4.IntJunitTests
If we want to run only the tests that failed during the last run, we could use the testQuick command instead.
Another interesting and versatile command we’ll use is testOnly. We can use it to run only the tests in a particular file, by specifying the global class name – for example, testOnly com.baeldung.scala.junit4.IntJunitTests – or a global pattern – testOnly *.IntJunitTests:
sbt:scala-test-junit4> testOnly *.IntJunitTests
...
[debug] Test run started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative finished, took 0.0 sec
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive finished, took 0.0 sec
[debug] Test run finished: 0 failed, 0 ignored, 2 total, 0.003s
[debug] Summary for JUnit not available.
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2
[debug] Passed tests:
[debug] com.baeldung.scala.junit4.IntJunitTests
We can also use it to run a single test:
sbt:scala-test-junit4> testOnly -- *.IntJunitTests.testOneIsPositive
...
[debug] Test run started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive finished, took 0.001 sec
[debug] Test run finished: 0 failed, 0 ignored, 1 total, 0.006s
[debug] Summary for JUnit not available.
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[debug] Passed tests:
[debug] com.baeldung.scala.junit4.IntJunitTests
Something important to notice is that all the arguments after — are passed to junit-interface. Since test doesn’t take arguments, we’ll use testOnly to pass JUnit-specific arguments. A full list of the available options can be found in the junit-interface documentation.
2.3. Our First Test Suite
One JUnit option is particularly interesting when dealing with test suites: –ignore-runners.
The –ignore-runners parameter allows specifying a list of runner class names to skip. The default value is org.junit.runners.Suite, used by JUnit to identify a test suite.** To run a test suite, we’ll override it with an empty list.
Let’s add another simple test:
package com.baeldung.scala.junit4
import org.junit.Test
import org.junit.Assert._
class StringJunitTests {
@Test
def testEmptyStringLeghtIsZero {
assertEquals("".length, 0)
}
}
We’ll now create a test suite using the JUnit @Suite.SuiteClasses annotation:
package com.baeldung.scala.junit4
import org.junit.runner.RunWith
import org.junit.runners.Suite
@RunWith(classOf[Suite])
@Suite.SuiteClasses(Array(classOf[IntJunitTests], classOf[StringJunitTests]))
class TypesTestSuite
Now, let’s run it:
sbt:scala-test-junit4> testOnly *.TypesTestSuite -- --ignore-runners=
...
[debug] Test run started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testMinusOneIsNegative finished, took 0.0 sec
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive started
[debug] Test com.baeldung.scala.junit4.IntJunitTests.testOneIsPositive finished, took 0.0 sec
[debug] Test com.baeldung.scala.junit4.StringJunitTests.testEmptyStringLengthIsZero started
[debug] Test com.baeldung.scala.junit4.StringJunitTests.testEmptyStringLengthIsZero finished, took 0.0 sec
[debug] Test run finished: 0 failed, 0 ignored, 3 total, 0.004s
[debug] Summary for JUnit not available.
[info] Passed: Total 3, Failed 0, Errors 0, Passed 3
[debug] Passed tests:
[debug] com.baeldung.scala.junit4.TypesTestSuite
As we can see, all the tests were correctly run.
Additionally, if we always want to pass a JUnit option to our runner, we can set it in our build.sbt:
testOptions += Tests.Argument(TestFrameworks.JUnit, "-v")
2.4. Assertions: ScalaTest Style
ScalaTest allows us to use its assertions, which can be more readable and concise, maintaining the rest of the JUnit syntax. We’ll start by adding the ScalaTest + JUnit dependency in build.sbt:
libraryDependencies ++= "com.github.sbt" % "junit-interface" % "0.13.3" % "test"
Different versions of ScalaTest + Junit work with different combinations of ScalaTest and JUnit*.*
Going back to our test, it’s enough to extend the test class with AssertionsForJUnit to allow mixing in the ScalaTest style assertions:
class STAssertionsTests extends AssertionsForJUnit {
private final val myInt = 1
@Test
def testAssertJUnitStyle() {
assertEquals(myInt, 1)
assertTrue(myInt > 0)
try {
myInt / 0
fail()
}
catch {
case e: ArithmeticException => // Expected
}
}
@Test
def testAssertScalaTestStyle() {
assert(myInt == 1)
assert(myInt.isValidInt)
intercept[ArithmeticException] {
myInt / 0
}
}
}
As we’ve shown in this example, the version using ScalaTest assertions is very concise.
3. JUnit 5
We can also use the more recent JUnit 5 framework for our tests. We only have to include the Jupiter Interface, which provides an implementation of SBT’s test interface for JUnit Jupiter.
Differently from the JUnit 4 library, Jupiter Interface is split into a library (jupiter-interface) and an sbt plugin (sbt-jupiter-interface).
This means that we’ll have to add the following to the plugins.sbt:
resolvers += Resolver.jcenterRepo
addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3")
Additionally, we’ll add the jupiter-interface to the dependencies:
libraryDependencies += "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test
We can now leverage the features available in JUnit 5. For example, let’s try out the @Disabled annotation:
package com.baeldung.junit5
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
class ExampleTests {
@Disabled("Not implemented yet")
@Test
def testNotImplemented() {}
}
Let’s run the test and show that it’s correctly skipped:
sbt:scala-test-junit-5> test
...
[info] Test run started (JUnit Jupiter)
[info] Test com.baeldung.junit5.ExampleTests#testNotImplemented() ignored: Not implemented yet
[debug] Test com.baeldung.junit5.ExampleTests finished, took 0.014s
[debug] Test finished, took 0.04s
[info] Test run finished: 0 failed, 1 ignored, 1 total, 0.064s
[info] Passed: Total 1, Failed 0, Errors 0, Passed 0, Skipped 1
4. Conclusion
In this article, we’ve seen how easy is to use JUnit in Scala as well. Furthermore, ScalaTest allows us to mixin its concise assertions. This is a good option if, for any reason, we’d like to maintain the JUnit syntax but leverage the expressiveness of the ScalaTest.
As usual, the code samples use here for JUnit 4 and JUnit 5 are available over on GitHub.