← Kembali ke kursus
Pelajaran kursus · Go
Pelajaran 53

Unit test & table-driven test

Uji fungsi otomatis dengan paket testing.

Pahami konsepnya

Tes Go hidup di berkas berakhiran _test.go di samping kode yang diuji. Setiap tes adalah fungsi bernama TestXxx (awalan Test wajib) yang menerima *testing.T. Jalankan semuanya dengan go test ./... — hasilnya lulus/gagal per paket, plus go test -v untuk rincian.

Kegagalan dilaporkan lewat t.Error / t.Errorf (menandai gagal tapi lanjut) atau t.Fatal / t.Fatalf (berhenti di titik itu). Assert bawaan memang tak ada — pola idiomatisnya if hasil != harapan { t.Errorf(...) } yang menampilkan dua nilai bersebelahan.

Bentuk dewasa di komunitas Go: table-driven test — semua kasus didefinisikan sebagai slice struct {nama, masukan, harapan}, lalu satu loop menjalankan semuanya lewat t.Run(nama, ...) yang membuat subtes terpisah. Menambah kasus baru cukup menambah baris tabel, tanpa menyalin logika. Subtes bisa dijalankan tunggal: go test -run TestBagi/pembagi_nol.

Coba contoh kode

package matematik

import "testing"

func Bagi(a, b int) (int, error) {
    if b == 0 {
        return 0, ErrPembagiNol
    }
    return a / b, nil
}

var ErrPembagiNol = errPembagiNol{}
type errPembagiNol struct{}
func (errPembagiNol) Error() string { return "pembagi tidak boleh nol" }

func TestBagi(t *testing.T) {
    hasil, err := Bagi(10, 2)
    if err != nil || hasil != 5 {
        t.Errorf("Bagi(10,2) = %d, %v; ingin 5, nil", hasil, err)
    }
}

Berkas matematik_test.go menguji fungsi di paket yang sama. t.Errorf menandai kegagalan TANPA menghentikan tes — beberapa kasus bisa diperiksa berurutan dan semuanya terlapor sekali jalan. Pesannya mencantumkan hasil dan harapan agar kegagalan langsung terbaca penyebabnya.

Table-driven test

func TestBagiTabel(t *testing.T) {
    kasus := []struct {
        nama             string
        a, b, harapan    int
        inginError       bool
    }{
        {"biasa", 10, 2, 5, false},
        {"tak bulat", 7, 2, 3, false},
        {"pembagi nol", 7, 0, 0, true},
        {"negatif", -9, 3, -3, false},
    }

    for _, k := range kasus {
        t.Run(k.nama, func(t *testing.T) {
            hasil, err := Bagi(k.a, k.b)
            if k.inginError && err == nil {
                t.Fatalf("%s: ingin error, dapat %d", k.nama, hasil)
            }
            if !k.inginError && hasil != k.harapan {
                t.Errorf("%s: dapat %d, ingin %d", k.nama, hasil, k.harapan)
            }
        })
    }
}

Empat kasus hidup dalam satu tabel; loop menjalankan masing-masing sebagai subtes bernama. Kasus "pembagi nol" menguji jalur error eksplisit — error yang DIHARAPkan adalah bagian kontrak fungsi, bukan kecelakaan. Tambahkan kasus baru dengan menambah baris, dan -run memungkinkan mengeksekusi hanya satu.

Latihan singkat

Uji: jalankan empat kasus pada tabel dan bandingkan dengan harapan — program harus mencetak persis `4/4 lulus`.