ScalaTest and testing styles

Versions: ScalaTest 3.0.3

At first glance the wide choice of testing families in Scala can scary. After all in JUnit and other xUnit frameworks, the choice of tests declaration is limited. Hopefully after some digging ScalaTest's testing styles become more obvious to understand and to use.

This post describes the test families available in ScalaTest framework. It's divided in 3 parts. The first one represents the code used in the examples. The second one lists and explains testing styles available in the framework. The final part analyzes which styles and how are used in 2 important Open Source Scala projects: Apache Spark and Akka.

Tested code

To illustrate test families we'll use the code below representing a simplified e-commerce shopping cart:

case class ShoppingCart(products: HashSet[Product] = new HashSet[Product](),
                        voucherCode: VoucherCode = ShoppingCart.NoVoucherCode,
                        deliveryMode: Option[DeliveryMode] = None) {

  def totalPrice: Double = ( + deliveryMode.get.price) * voucherCode.discountPercentage

  def addProduct(product: Product): ShoppingCart = {
    val newProductsList = products + product
    this.copy(products = newProductsList)

  def removeProduct(product: Product): ShoppingCart = {
    val newProductsList = products - product
    this.copy(products = newProductsList)

  def setDeliveryMode(newDeliveryMode: DeliveryMode): ShoppingCart = this.copy(deliveryMode=Some(newDeliveryMode))

  def setVoucherCode(newVoucherCode: VoucherCode): ShoppingCart = this.copy(voucherCode=newVoucherCode)


object ShoppingCart {
  val NoVoucherCode = VoucherCode("no_code", 1.0)

case class Product(name: String, price: Double)

case class VoucherCode(code: String, discountPercentage: Double) {
  assert(discountPercentage <= 1.0, "The discount percentage can't be greater than 100%")

case class DeliveryMode(name: String, price: Double)

ScalaTest testing styles

ScalaTest is Scala's popular testing framework. It's characterized by a lot of flexibility in terms of tests definition. This flexibility is brought mainly by the testing styles that can be adapted as well for classical unit testing (xUnit-based) as for more descriptive BDD. These testing styles are expressed by following style traits:

Testing styles in OS projects

Let's start the analysis of the OS projects using ScalaTest with Apache Spark. The tests in this data processing framework are organized around org.apache.spark.SparkFunSuite . This abstract class extends FunSuite and mixes with BeforeAndAfterAll, ThreadAudit and Logging traits. The purpose of SparkFunSuite is to handle the functionalities shared by the tests as: setting spark.testing to true or cleaning accumulators context. A single exception not using SparkFunSuite directly is org.apache.spark.sql.RowTest that doesn't need the Spark context to execute the tests.

Spark also works with other testing styles as FlatSpec, FunSpec and WordSpec. But, unlike FunSuite, they're not directly used in the written tests. Instead they are defined as the assertions checking if SharedSparkContext works correctly if other testing styles than FunSuite are used by the client applications.

In Akka we can find the preference for WordSpec. It's either used as a base class or, as in the case of SparkFunSuite, as a parent for more specific Akka testing styles (e.g. akka.testkit.typed.scaladsl.AbstractActorSpec). But it was not always true. In the past some tests were using FlatSpec. But it was not estimated good for the codebase consistency and some work was done to keep only WordSpec.

At first glance ScalaTest testing styles seem to be complicated because of their quantity. However after some digging the ideas become more clear. As we could learn, a part of styles are BDD-oriented and another part is more appropriate for the teams coming from classical xUnit ground. Even though the variety of styles, the main OS projects based on Scala prefers always 1 testing style. For Apache Spark it's FunSuite brought with SparkFunSuite base class. Akka in its turn prefers WordSpec.