89. Pengenalan Software Testing & Package testing
Konsep pengujian otomatis software dan pengenalan package testing bawaan Go.
Mengapa Software Testing Penting?
Software Testing adalah proses otomatisasi untuk memverifikasi bahwa kode program bekerja sesuai ekspektasi.
Go menyediakan package standar testing dan CLI go test tanpa perlu framework eksternal.
Aturan Wajib Unit Test di Go:
- Nama file test HARUS diakhiri
_test.go - Nama fungsi test HARUS diawali
Test(contoh:TestSayHello) - Parameter fungsi HARUS
(t *testing.T)
Contoh program
package main
import "fmt"
func Add(a, b int) int {
return a + b
}
func main() {
result := Add(3, 5)
if result == 8 {
fmt.Println("PASS: Add(3,5) =", result)
} else {
fmt.Println("FAIL: expected 8, got", result)
}
}
90. Membuat Unit Test Pertama (TestXxx)
Struktur fungsi TestXxx(t *testing.T) dan eksekusi via go test -v.
Struktur Fungsi Unit Test
Fungsi unit test ditulis di berkas _test.go dengan signature func TestXxx(t *testing.T).
Perintah CLI:
go test— Jalankan semua test di packagego test -v— Mode verbose (detail nama test)go test -v ./...— Tes rekursif seluruh sub-package
Contoh program
package main
import "fmt"
func SayHello(name string) string {
return "Hello " + name
}
func main() {
output := SayHello("Eko")
if output == "Hello Eko" {
fmt.Println("PASS: SayHello('Eko') =", output)
} else {
fmt.Println("FAIL: unexpected output:", output)
}
}
91. Menggagalkan Test (Fail, FailNow, Error, Fatal)
Perbedaan t.Fail(), t.FailNow(), t.Error(), dan t.Fatal().
Method Penggagalan Unit Test
| Method | Fungsi | Lanjut Eksekusi? |
|---|---|---|
t.Fail() | Tandai gagal | YA |
t.FailNow() | Tandai gagal + hentikan | TIDAK |
t.Error() | Log + Fail | YA |
t.Fatal() | Log + FailNow | TIDAK |
Gunakan t.Fatal() jika tes berikutnya bergantung pada hasil baris ini.
Contoh program
package main
import "fmt"
func main() {
fmt.Println("=== Perbedaan Method Penggagalan ===")
fmt.Println("t.Fail() -> tandai gagal, LANJUT eksekusi")
fmt.Println("t.FailNow() -> tandai gagal, BERHENTI seketika")
fmt.Println("t.Error() -> log pesan + t.Fail()")
fmt.Println("t.Fatal() -> log pesan + t.FailNow()")
}
92. Assertion dengan Testify (assert & require)
Library testify/assert dan require untuk pengujian yang lebih bersih.
Library Testify
| Package | Jika Gagal | Padanan |
|---|---|---|
assert.Equal(t, expected, actual) | Lanjut eksekusi | t.Error() |
require.Equal(t, expected, actual) | Langsung berhenti | t.Fatal() |
Contoh program
package main
import "fmt"
func Discount(price float64) float64 {
return price * 0.9
}
func main() {
result := Discount(100.0)
fmt.Println("Diskon 10% dari 100 =", result)
fmt.Println("Testify: assert.Equal(t, 90.0, Discount(100.0))")
}
93. Skip Test & TestMain (Setup/Teardown)
Melewati test dengan t.Skip() dan mengelola setup/teardown global dengan TestMain(m *testing.M).
t.Skip() & TestMain
t.Skip() melompati test tanpa menggagalkannya. TestMain(m *testing.M) menangani setup sebelum dan teardown sesudah seluruh test berjalan.
Contoh program
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("OS:", runtime.GOOS)
fmt.Println("t.Skip() melompati test jika kondisi tidak terpenuhi")
fmt.Println("TestMain(m *testing.M) untuk setup/teardown global")
}
94. Sub Tests (t.Run)
Membuat pengujian bertingkat / sub-test menggunakan t.Run().
Sub Test
t.Run("NamaSubTest", func(t *testing.T){...}) memecah satu fungsi test menjadi beberapa skenario. Jalankan spesifik sub-test: go test -v -run=TestSayHello/Eko.
Contoh program
package main
import "fmt"
func main() {
fmt.Println("Sub Test Structure:")
fmt.Println("t.Run(\"Eko\", func(t *testing.T) { ... })")
fmt.Println("t.Run(\"Budi\", func(t *testing.T) { ... })")
fmt.Println("CLI: go test -v -run=TestSayHello/Eko")
}
95. Table-Driven Testing
Teknik paling populer di Go untuk menguji puluhan kombinasi input-output menggunakan slice of struct.
Table-Driven Testing
Susun skenario pengujian di dalam slice struct, lalu iterasi dengan for dan t.Run(). Menambah skenario baru cukup tambah elemen struct.
Contoh program
package main
import "fmt"
type TestData struct {
name string
input int
expected int
}
func MultiplyByTwo(n int) int { return n * 2 }
func main() {
tests := []TestData{
{"Positif", 5, 10},
{"Nol", 0, 0},
{"Negatif", -3, -6},
}
for _, tt := range tests {
result := MultiplyByTwo(tt.input)
status := "PASS"
if result != tt.expected { status = "FAIL" }
fmt.Printf("[%s] %s: %d -> %d (expected %d)\n", status, tt.name, tt.input, result, tt.expected)
}
}
96. Mocking Dependencies (testify/mock)
Membuat objek tiruan (Mock) untuk mengisolasi unit test dari dependency eksternal.
Mocking
Saat menguji Service Layer, jangan akses database sungguhan — buat Mock Object yang meniru Repository. Library testify/mock menyediakan mock.Mock dan method On().Return().
Contoh program
package main
import "fmt"
func main() {
fmt.Println("=== MOCKING WORKFLOW ===")
fmt.Println("1. Buat MockRepository embed mock.Mock")
fmt.Println("2. mockRepo.On(\"FindById\", \"1\").Return(user)")
fmt.Println("3. mockRepo.AssertExpectations(t)")
}
97. Benchmarking (BenchmarkXxx & b.N)
Mengukur performa kecepatan dan konsumsi memori kode Go.
Benchmark di Go
Fungsi benchmark diawali Benchmark, parameter (b *testing.B), loop for i := 0; i < b.N; i++. Jalankan: go test -v -bench=. -benchmem.
Contoh program
package main
import "fmt"
func main() {
fmt.Println("Benchmark Syntax:")
fmt.Println("func BenchmarkHello(b *testing.B) {")
fmt.Println(" for i := 0; i < b.N; i++ {")
fmt.Println(" HelloWorld(\"Eko\")")
fmt.Println(" }")
fmt.Println("}")
fmt.Println("CLI: go test -v -bench=. -benchmem")
}
98. Sub Benchmark & Table Benchmark
Membandingkan performa beberapa algoritma dengan b.Run() dan parameterized benchmarks.
Sub & Table Benchmark
b.Run() membandingkan algoritma secara berdampingan. Table benchmark menguji performa pada berbagai skala input (Small/Medium/Large).
Contoh program
package main
import "fmt"
func main() {
fmt.Println("Sub Benchmark: b.Run(\"ConcatPlus\", func(b *testing.B){...})")
fmt.Println("Table Benchmark: menguji variasi skala data input")
fmt.Println("Flag: -benchmem menampilkan B/op dan allocs/op")
}