Belajar Scala - Testing (MUnit/ScalaTest) & Debug
Episode 10 of 23

Belajar Scala - Testing (MUnit/ScalaTest) & Debug

Membangun kebiasaan testing sejak awal: MUnit sebagai framework ringan dengan assertEquals, struktur suite test di sbt, pengenalan ScalaTest untuk gaya BDD, serta debugging interaktif dengan breakpoints di Metals dan eksperimen cepat di REPL.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 9 kalian membangun proyek sbt pertama, sekarang kita memasang pagar pengaman: testing. Jika episod-episode sebelumnya mengajarkan menulis kode, episode ini mengajarkan membuktikan kode itu benar — dan itu justru lebih penting di dunia produksi.

Mengapa testing penting di Scala? Karena bahasa ini memberi kalian type system yang kuat, tetapi tipe tidak menjamin logika benar. Keberuntungannya, sifat pure function yang kita pelajari sejak episode 4 membuat kode Scala sangat mudah diuji: fungsi murni selalu memberi output yang sama untuk input yang sama — ideal untuk assertion yang deterministik.

Setup MUnit di sbt

MUnit adalah framework testing Scala yang ringan dan modern. Tambahkan ke build.sbt:

Tambah MUnit ke build.sbt
lazy val root = project
  .in(file("."))
  .settings(
    name := "scala-app",
    version := "0.1.0",
    scalaVersion := "3.3.8",
    libraryDependencies ++= Seq(
      "org.scalameta" %% "munit" % "1.0.0" % Test
    )
  )

Suffix % Test berarti dependency hanya dipakai saat fase test — tidak ikut ke artefak produksi.

Kode produksi sederhana yang akan kita uji:

src/main/scala/Calculator.scala
object Calculator:
  def add(a: Int, b: Int): Int = a + b
  def divide(a: Int, b: Int): Either[String, Int] =
    if b == 0 then Left("pembagian nol")
    else Right(a / b)

Test MUnit adalah class yang meng-extends FunSuite, dengan method test:

src/test/scala/CalculatorSuite.scala
class CalculatorSuite extends munit.FunSuite:
 
  test("add menjumlahkan dua angka") {
    assertEquals(Calculator.add(2, 3), 5)
  }
 
  test("add menangani angka negatif") {
    assertEquals(Calculator.add(-1, -1), -2)
  }
 
  test("divide mengembalikan Left untuk pembagian nol") {
    assertEquals(Calculator.divide(10, 0), Left("pembagian nol"))
  }
 
  test("divide menghitung dengan benar") {
    assertEquals(Calculator.divide(10, 2), Right(5))
  }

Jalankan dengan sbt test:

Output sbt test
[info] CalculatorSuite:
[info]   + add menjumlahkan dua angka
[info]   + add menangani angka negatif
[info]   + divide mengembalikan Left untuk pembagian nol
[info]   + divide menghitung dengan benar
[info] All tests passed.

MUnit menampilkan hasil per-test, dan assertion gagal memberikan pesan kontekstual yang jelas: nilai yang diharapkan vs yang aktual.

Assertion Penting di MUnit

AssertionFungsi
assertEquals(actual, expected)Kesetaraan nilai
assert(condition)Kondisi boolean
assertNotEquals(a, b)Nilai tidak sama
intercept[Exception]Memastikan exception dilempar
fail("pesan")Gagalkan test secara eksplisit
Assertion tambahan
test("fail untuk kasus tak terduga") {
  assert(Calculator.add(0, 0) == 0)
}
 
test("intercept memvalidasi exception") {
  intercept[ArithmeticException] {
    1 / 0
  }
}

Struktur Test yang Baik

Test yang baik mengikuti pola AAA — Arrange, Act, Assert:

Pola Arrange-Act-Assert
test("hitung total keranjang") {
  val items = List(5000.0, 3000.0, 2000.0)  // Arrange
  val total = items.sum                       // Act
  assertEquals(total, 10000.0)                // Assert
}

Beberapa prinsip untuk diingat:

  • Satu test = satu perilaku. Nama test menjelaskan perilaku, bukan "test1".
  • Test harus independen: tidak bergantung pada urutan eksekusi.
  • Fokus pada fungsi murni: semakin banyak logika di pure function, semakin mudah diuji.
  • Gunakan property-based testing untuk kasus luas — itu topik episode 19.

ScalaTest: Gaya BDD

ScalaTest adalah framework lain yang menawarkan berbagai gaya. Yang paling populer adalah AnyFlatSpec dengan gaya BDD:

ScalaTest dengan gaya BDD
import org.scalatest.funsuite.AnyFunSuite
 
class CalculatorSpec extends AnyFunSuite:
  test("Calculator.add harus menjumlahkan") {
    assert(Calculator.add(2, 3) == 5)
  }
ScalaTest gaya FlatSpec
import org.scalatest.flatspec.AnyFlatSpec
 
class CalculatorSpec extends AnyFlatSpec:
  "Calculator.add" should "menjumlahkan dua angka" in {
    assert(Calculator.add(2, 3) == 5)
  }

Pilih satu framework dan konsisten: MUnit untuk project yang ingin ringan dan modern, ScalaTest jika kalian suka gaya BDD yang deskriptif. Keduanya bisa dipakai bersamaan dalam satu proyek.

Tip

Untuk series ini, MUnit adalah pilihan utama — ringan, cepat, dan assertion-nya menampilkan diff yang jelas saat gagal. Pola yang dipelajari (suite, assertion, AAA) 100% berlaku juga di ScalaTest.

Debugging dengan Metals

Breakpoints di VS Code

Metals mengintegrasikan DAP (Debug Adapter Protocol) ke VS Code. Alurnya:

  1. Buka file kode di VS Code.
  2. Klik gutter di samping nomor baris untuk menempatkan breakpoint (titik merah).
  3. Pilih "Run" dan jalankan test atau aplikasi dalam mode debug.
  4. Saat eksekusi sampai breakpoint, kalian bisa:
    • Melihat nilai variabel di panel Variables.
    • Melangkah (Step Over / Step Into / Step Out).
    • Melihat call stack dan ekspresi di Watch.
Contoh kode yang akan di-debug
@main def run(): Unit =
  val prices = List(5000.0, 3000.0)
  val total = prices.sum
  println(s"Total: $total")   // pasang breakpoint di baris ini

Debugging di REPL

Untuk masalah kecil, REPL sering lebih cepat daripada breakpoint — tiru logika yang bermasalah di sesi interaktif:

Debug cepat di REPL
scala> List(1,2,3).map(_ * 2)
val res0: List[Int] = List(2, 4, 6)
 
scala> :quit

Jika hasil REPL sama dengan yang kalian harapkan tetapi program produksi tidak, masalahnya ada di alur sekitarnya — bukan di logika murni tersebut. Pola ini memanfaatkan kekuatan pure function: reproduksi langsung tanpa setup kompleks.

Warning

Saat debugging dengan breakpoint, pastikan Metals sudah mengimport project (banner "Import build" di pojok kanan bawah). Tanpa import, breakpoint sering tidak aktif karena bytecode yang dijalankan berbeda dengan source yang kalian lihat.

Common Pitfalls

  • Test bergantung pada urutan — tulis test yang independen; sbt test bisa menjalankan dengan paralelisme.
  • Assertion yang salah arahassertEquals(actual, expected): nilai aktual di kiri, harapan di kanan, agar pesan error terbaca benar.
  • Tidak mengetes edge case — pembagian nol, list kosong, input negatif adalah sumber bug klasik.
  • Meletakkan kode produksi di folder test — file di src/test tidak boleh dipakai dari src/main.

Penutup

Inti yang harus dibawa pulang:

  • MUnit adalah framework testing ringan: FunSuite, test, dan assertEquals.
  • Tambah dependency dengan suffix % Test di build.sbt; jalankan dengan sbt test.
  • Struktur test mengikuti pola AAA — Arrange, Act, Assert — dengan satu perilaku per test.
  • ScalaTest menawarkan gaya BDD; pilih satu dan konsisten.
  • Metals + breakpoints untuk debugging, REPL untuk reproduksi cepat logika murni.

Di episode 11 selanjutnya, kita memasuki topik yang membedakan Scala dari bahasa JVM lain: functional effects dengan cats-effect dan ZIO — komposisi program pure, resource safety, dan cancellation. Sampai jumpa di episode 11!

Belajar Scala - Testing (MUnit/ScalaTest) & Debug | Belajar Scala