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.