Testing And Benchmarking in Scala

Here’s the translation of the Go testing and benchmarking example to Scala, formatted for Hugo:

Unit testing is an important part of writing principled Scala programs. The org.scalatest package provides the tools we need to write unit tests, and the sbt test command runs tests.

For the sake of demonstration, this code is in the default package, but it could be any package. Testing code typically lives in the same package as the code it tests.

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

// We'll be testing this simple implementation of an integer minimum.
def intMin(a: Int, b: Int): Int = if (a < b) a else b

class IntMinSpec extends AnyFlatSpec with Matchers {
  "IntMin" should "return the minimum of two numbers" in {
    val result = intMin(2, -2)
    result shouldBe -2
  }

  // Writing tests can be repetitive, so it's idiomatic to use a table-driven style,
  // where test inputs and expected outputs are listed in a table and a single loop
  // walks over them and performs the test logic.
  it should "work for various inputs" in {
    val testCases = Table(
      ("a", "b", "expected"),
      (0, 1, 0),
      (1, 0, 0),
      (2, -2, -2),
      (0, -1, -1),
      (-1, 0, -1)
    )

    forAll(testCases) { (a: Int, b: Int, expected: Int) =>
      intMin(a, b) shouldBe expected
    }
  }
}

For benchmarking in Scala, we can use a library like scalameter. Here’s an example of how you might set up a benchmark:

import org.scalameter.api._

object IntMinBenchmark extends Bench.LocalTime {
  val sizes = Gen.range("size")(300000, 1500000, 300000)

  val ranges = for {
    size <- sizes
  } yield 0 until size

  performance of "IntMin" in {
    measure method "min" in {
      using(ranges) in { r =>
        r.foldLeft(Int.MaxValue)(intMin)
      }
    }
  }
}

To run all tests in the current project:

$ sbt test
[info] IntMinSpec:
[info] IntMin
[info] - should return the minimum of two numbers
[info] - should work for various inputs
[info] Run completed in 234 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.

To run benchmarks (assuming you’ve set up ScalaMeter correctly):

$ sbt "test:run-main IntMinBenchmark"
[info] Running IntMinBenchmark 
[info] ::Benchmark IntMin.min::
[info] cores: 8
[info] hostname: localhost
[info] jvm-name: Java HotSpot(TM) 64-Bit Server VM
[info] jvm-vendor: Oracle Corporation
[info] jvm-version: 1.8.0_201
[info] os-arch: x86_64
[info] os-name: Mac OS X
[info] Parameters(size -> 300000): 2.546668 ms
[info] Parameters(size -> 600000): 5.06376 ms
[info] Parameters(size -> 900000): 7.59084 ms
[info] Parameters(size -> 1200000): 10.118472 ms
[info] Parameters(size -> 1500000): 12.631644 ms

In Scala, we use libraries like ScalaTest for unit testing and ScalaMeter for benchmarking. The concepts are similar to Go’s testing package, but the syntax and specific features differ. ScalaTest provides a variety of testing styles, and we’ve used FlatSpec in this example. For benchmarking, ScalaMeter offers more sophisticated tools for performance testing compared to Go’s simple benchmark functions.