I compared JUnit & ScalaTest and found ScalaTest to be a better option for writing unit test cases for Scala application. It completely makes sense to use scala based framework for unit testing in Scala.
Recently I was leading a team developing in Scala. The code was intended to run on Databricks. It’s a big data setup. However, it is not a big data or machine learning project. We were not using notebooks but, building JARs and deploying them in Databricks clusters.
Due to the application’s resemblance with Java applications, we tried using JUnit to write our test cases and scoverage to produce the test coverage report. Soon, we realized that Scala and JUnit are not exactly compatible. It gives error as shown below:
Error:(89, 48) type mismatch;
found : () => Unit required: org.junit.jupiter.api.function.Executable
assertThrows(classOf[MyException], ...
You can still build proper unit test but now your test cases may need more lines of code to catch the exception and verify it’s type.
We decided to give ScalaTest a shot. This worked in the first go. Particularly, I liked ScalaTest’s support for different testing styles. The documentation for ScalaTest recommended FlatSpec style for first-timers moving from xUnit to BDD. We liked the Style and all the developers were in agreement.
The best part of scalaTest is its documentation. It has a very comprehensive user guide, The guide explains the different styles we can choose, the Maven dependencies & plugins to use, and has enough examples showing how to use different features of ScalaTest.
Both the development team and the QA team were happy to see the documentation generated during test execution. Additionally, I liked ScalaTest’s small dependency footprint and the various options it provides to select the right dependency.
Quick Reference
Maven POM file changes
In your pom.xml file you need to add the plugns as shown below.
<!-- disable surefire -- >
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- enable scalatest -- >
<plugin>
<groupId>org.scalatest</groupId>
<artifactId>scalatest-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
<junitxml>.</junitxml>
<filereports>WDF TestSuite.txt</filereports>
</configuration>
<executions>
<execution>
<id>test</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
As part of dependency you need to add the following:
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_2.13</artifactId>
<version>3.2.7</version>
<scope>test</scope>
</dependency>
However, when you add the above dependencies it downloads many dependent components. However, we really dont need everything that ScalaTest has to offer. For example, if you choose to use FlatSpec style there is no point to include other styles as part of your dependencies. I like when my builds are lean. Also, there is another drawback, you wouldn’t want your team to used different styles while writing unit test cases. That will be highly confusing.
Below is what I prefer when using FlatSpec style.
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest-flatspec_2.11</artifactId>
<version>3.2.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest-shouldmatchers_2.11</artifactId>
<version>3.3.0-SNAP2</version>
<scope>test</scope>
</dependency>
You should verify the versions to use from https://mvnrepository.com/artifact/org.scalatest/scalatest-flatspec, as there are different versions based on scala version you are using.
Additionally, you need to add dependency for shouldmatcher. As we are not using scalatest which includes everything we had to add two dependencies to ensure our unit tests can be compiled.
Writing Unit Test cases
We wil be using flatSpec style for our test cases. It facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify. Here’s an example FlatSpec
package org.some.examples.flatspecTest
import org.scalatest.flatspec.AnyFlatSpec
class SetSpec extends AnyFlatSpec {
behavior of "An empty Set"
it should "have size 0" in {
assert(Set.empty.size === 0)
}
it should "produce NoSuchElementException when head is invoked" in {
assertThrows[NoSuchElementException] {
Set.empty.head
}
}
}
Instead of using a behavior of
clause, you can alternatively use a shorthand syntax in which you replace the first it
with the subject string, like this:
package org.some.examples.flatspecTest
import org.scalatest.flatspec.AnyFlatSpec
class SetSpec extends AnyFlatSpec {
"An empty Set" should "have size 0" in {
assert(Set.empty.size === 0)
}
it should "produce NoSuchElementException when head is invoked" in {
assertThrows[NoSuchElementException] {
Set.empty.head
}
}
}
Running either of the two previous versions of SetSpec
in the Scala interpreter would yield:
An empty Set - should have size 0 - should produce NoSuchElementException when head is invoked
I like this verbose output, which acts as a documentation of the test that we are performing.
Lastly, I found that “it” and “they” can be used interchangeably. This is to support subjects being tested that are plural in nature. And in case you want to suppress or ignore a test case then replace “it”/”they” with “ignore”. This can come in handy, even though I don’t advocate ignoring test cases.
I would suggest, to go through https://www.scalatest.org/scaladoc/3.2.7/org/scalatest/flatspec/AnyFlatSpec.html to get a better understanding of FlatSpec style.