Episode ini mengasah idiom error handling Go dengan error, fmt.Errorf, errors.Is, dan errors.As. Kalian juga menulis unit test dengan package testing, table-driven tests, dan benchmark, lalu mendebugs kode dengan go test dan delve.

Di episode 3 kalian sudah tahu pola if err != nil. Sekarang kita dalamkan menjadi sistem yang utuh: bagaimana error dibentuk, dibandingkan, dan di-debug. Di sisi lain, program yang tidak diuji bukanlah program produksi — Go menyediakan tooling testing dan benchmarking langsung dari standard library.
Episode 6 membahas tiga pilar kualitas kode Go: idiom error handling lanjutan dengan errors.Is dan errors.As, penulisan unit test dengan package testing termasuk table-driven tests dan benchmark, serta teknik debugging dengan go test dan delve.
Error di Go adalah nilai biasa yang diimplementasikan lewat interface error dengan satu method Error() string. Untuk membentuk error dengan konteks, gunakan fmt.Errorf dan bungkus error asli dengan %w:
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("data tidak ditemukan")
func cariData(id int) (string, error) {
if id != 42 {
return "", fmt.Errorf("cari id %d: %w", id, ErrNotFound)
}
return "data rahasia", nil
}Variabel sentinel seperti ErrNotFound memungkinkan pemanggil membandingkan error tanpa membongkar string-nya.
errors.Is menelusuri rantai error yang dibungkus %w untuk memeriksa apakah error tertentu ada di dalamnya. errors.As mengambil error dari rantai ke tipe target:
_, err := cariData(1)
if errors.Is(err, ErrNotFound) {
fmt.Println("kategori: not found")
}
var target *TipeCustomError
if errors.As(err, &target) {
fmt.Println("detail kustom:", target.Kode)
}Aturan emasnya: gunakan errors.Is untuk sentinel error, dan errors.As untuk error bertipe kustom. Jangan pernah membandingkan error dengan == kecuali variabelnya memang identik.
Bungkus error setiap kali melewati batas lapisan arsitektur — dari repository ke service, dari service ke handler. Jangan membungkus error yang sudah cukup informatif, dan hindari kata-kata berlebihan seperti "error ketika ... terjadi error". Konvensi umum: huruf kecil di awal pesan karena pesan sering digabungkan.
File test berakhiran _test.go dan berada di direktori yang sama dengan source. Fungsi test diawali Test dan menerima parameter *testing.T. Jalankan dengan go test ./....
package kalkulator
import "testing"
func TestTambah(t *testing.T) {
hasil := Tambah(2, 3)
if hasil != 5 {
t.Errorf("Tambah(2, 3) = %d, harap 5", hasil)
}
}t.Errorf menandai test gagal dan melanjutkan eksekusi, sedangkan t.Fatalf menghentikan test seketika. Untuk log biasa saat test berjalan, gunakan t.Logf.
Pola ini adalah standar de facto di ekosistem Go: satu slice berisi kasus uji, lalu satu perulangan menjalankan semuanya. Menambah kasus baru cukup menambah satu baris di tabel.
func TestBagi(t *testing.T) {
cases := []struct {
nama string
a, b float64
ingin float64
harapErr bool
}{
{nama: "pembagian normal", a: 10, b: 2, ingin: 5},
{nama: "pembagian nol", a: 10, b: 0, harapErr: true},
}
for _, tc := range cases {
t.Run(tc.nama, func(t *testing.T) {
hasil, err := Bagi(tc.a, tc.b)
if tc.harapErr && err == nil {
t.Fatal("harapkan error")
}
if !tc.harapErr && hasil != tc.ingin {
t.Errorf("hasil = %v, harap %v", hasil, tc.ingin)
}
})
}
}Subtest t.Run membuat setiap kasus dilaporkan terpisah, sehingga test yang gagal langsung terlihat jelas di output go test -v.
Fungsi benchmark diawali Benchmark dan menerima *testing.B. Perulangan b.N dijalankan secara adaptif oleh framework hingga akurat.
func BenchmarkTambah(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Tambah(i, 1)
}
}Jalankan dengan flag -bench dan -benchmem untuk melihat alokasi memori:
go test -bench=. -benchmem ./...Metrik yang muncul: jumlah iterasi, durasi per operasi, byte yang dialokasikan per operasi, dan jumlah alokasi. Benchmark ini menjadi modal awal untuk episode 13 tentang optimasi performansi.
go test -v menampilkan nama test yang berjalan dan output t.Logf. -run menyaring test berdasarkan pola nama, sangat berguna saat satu paket berisi ratusan test.
go test -run TestBagi -v ./...Ketika fmt.Println tidak cukup, gunakan delve — debugger resmi untuk Go. Install dengan go install github.com/go-delve/delve/cmd/dlv@latest, lalu mulai sesi debugging. Delve terintegrasi dengan editor lewat DAP, sehingga kalian bisa memasang breakpoint langsung dari VS Code atau Neovim.
dlv debug main.goDi dalam sesi: break main.go:10 untuk breakpoint, continue untuk lanjut, print variabel untuk inspeksi, dan next untuk eksekusi per baris. Keluar dengan exit.
Episode 6 menutup pilar kualitas kode Go: membentuk dan membandingkan error dengan errors.Is serta errors.As, menulis unit test dan table-driven tests dengan package testing, mengukur performa dengan benchmark, serta mendebugs dengan go test dan delve.
Inti yang harus dibawa pulang:
%w untuk menjaga rantai.errors.Is untuk sentinel, errors.As untuk tipe kustom._test.go dan dijalankan dengan go test.t.Run adalah standar industri.*testing.B dan flag -benchmem.Di episode 7 selanjutnya kita akan membahas konfigurasi aplikasi, environment variables, dan flags — membaca konfigurasi dari environment dan file .env, menangani CLI flags dengan paket flag, framework modern seperti cobra, serta best practice konfigurasi untuk local, staging, dan production.