Testing

Other styles: AnyFlatSpec ("X should Y in {...}"), AnyWordSpec, AnyFreeSpec, AsyncFunSuite for Futures.

24.1 MUnit

class MathSuite extends munit.FunSuite:
  test("addition") {
    assertEquals(1 + 1, 2)
  }
  test("exception") {
    intercept[ArithmeticException](1 / 0)
  }
  test("option") {
    assert(List(1, 2).headOption.contains(1), "should contain 1")
  }

  val tmpDir = FunFixture[os.Path](
    setup = _ => os.temp.dir(),
    teardown = dir => os.remove.all(dir))
  tmpDir.test("uses fixture") { dir => assert(os.exists(dir)) }

24.2 ScalaTest (many styles)

Other styles: AnyFlatSpec ("X should Y in {...}"), AnyWordSpec, AnyFreeSpec, AsyncFunSuite for Futures.

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class StackSpec extends AnyFunSuite with Matchers:
  test("pop returns last pushed") {
    val s = scala.collection.mutable.Stack(1, 2)
    s.pop() shouldBe 2
    s should have size 1
    an[NoSuchElementException] should be thrownBy List.empty[Int].head
  }

24.3 Property-based testing with ScalaCheck

Custom generators: Gen.choose(1, 100), Gen.oneOf(...), Gen.listOf(...), Arbitrary. Integrations: munit-scalacheck, scalatestplus-scalacheck.

import org.scalacheck.Prop.forAll
import org.scalacheck.Properties

object ListProps extends Properties("List"):
  property("reverse twice") = forAll((xs: List[Int]) => xs.reverse.reverse == xs)
  property("concat length") = forAll((a: List[Int], b: List[Int]) => (a ++ b).length == a.length + b.length)

24.4 Other testing tools

weaver-test (for Cats Effect), ZIO Test, utest (Li Haoyi).

Mocking: Mockito (via mockito-scala), scalamock — but prefer fakes/stubs via traits.

Integration tests: testcontainers-scala.

Coverage: scoverage (sbt coverage test coverageReport).

🧪 Part II Exercises

Model a library system: case class Book, enum Genre, a Library class with add/find/remove returning Either.

Implement a sealed trait Expr (Num, Add, Mul, Neg, Var) with eval(env: Map[String, Int]) and simplify.

Write a custom extractor object IPv4 that destructures "192.168.0.1" into four ints.

Implement a binary search tree as a generic enum Tree[A] with insert, contains, and in-order traversal.

Count word frequencies in a file with Source and groupMapReduce; print top 10.

Use stackable traits to build a logger with Timestamped, Uppercase, and Filtered behaviors.

Write a for-comprehension validating a registration form (name, email, age) with Either.

Implement LazyList of primes (sieve) and print the first 20.

Add extension methods to Int: isPrime, factorial, digits.

Write MUnit tests and ScalaCheck properties for exercises 2 and 4.