Testing
Testing adalah bagian penting dari pengembangan software. Go menyediakan package testing untuk unit testing, benchmark, dan example testing.
Contoh Masalah
Bagaimana cara:
- Menulis unit test
- Melakukan benchmark
- Membuat example test
Penyelesaian
File: math.go
package math
// Add menambahkan dua angka
func Add(a, b int) int {
return a + b
}
// Subtract mengurangi dua angka
func Subtract(a, b int) int {
return a - b
}
// Multiply mengalikan dua angka
func Multiply(a, b int) int {
return a * b
}
// Divide membagi dua angka
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("tidak bisa membagi dengan nol")
}
return a / b, nil
}
File: math_test.go
package math
import (
"fmt"
"testing"
)
// 1. Basic unit test
func TestAdd(t *testing.T) {
got := Add(4, 6)
want := 10
if got != want {
t.Errorf("Add(4, 6) = %d; want %d", got, want)
}
}
// 2. Table-driven test
func TestOperations(t *testing.T) {
tests := []struct {
name string
a, b int
op string
want int
wantErr bool
}{
{"add positive numbers", 2, 3, "add", 5, false},
{"subtract positive numbers", 5, 3, "subtract", 2, false},
{"multiply positive numbers", 4, 3, "multiply", 12, false},
{"divide positive numbers", 6, 2, "divide", 3, false},
{"divide by zero", 4, 0, "divide", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got int
var err error
switch tt.op {
case "add":
got = Add(tt.a, tt.b)
case "subtract":
got = Subtract(tt.a, tt.b)
case "multiply":
got = Multiply(tt.a, tt.b)
case "divide":
got, err = Divide(tt.a, tt.b)
}
if (err != nil) != tt.wantErr {
t.Errorf("got error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("got %d, want %d", got, tt.want)
}
})
}
}
// 3. Benchmark test
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(4, 6)
}
}
// 4. Example test
func ExampleAdd() {
sum := Add(4, 6)
fmt.Println(sum)
// Output: 10
}
// 5. Parallel testing
func TestParallelAdd(t *testing.T) {
t.Parallel()
tests := []struct {
a, b, want int
}{
{2, 3, 5},
{4, 6, 10},
{-2, 3, 1},
{0, 0, 0},
}
for _, tt := range tests {
tt := tt // capture range variable
t.Run(fmt.Sprintf("%d+%d=%d", tt.a, tt.b, tt.want), func(t *testing.T) {
t.Parallel()
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
// 6. Setup and teardown
func TestMain(m *testing.M) {
// Setup code
fmt.Println("Starting tests...")
// Run tests
code := m.Run()
// Teardown code
fmt.Println("Tests completed.")
// Exit
os.Exit(code)
}
// 7. Helper function
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestDivideWithHelper(t *testing.T) {
result, err := Divide(6, 2)
assertNoError(t, err)
if result != 3 {
t.Errorf("Divide(6, 2) = %d; want 3", result)
}
}
Cara Menjalankan Test
# Run all tests
go test
# Run tests with verbose output
go test -v
# Run specific test
go test -run TestAdd
# Run benchmarks
go test -bench=.
# Run tests with coverage
go test -cover
# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
Penjelasan Kode
Basic Testing
- Nama fungsi TestXxx
- Parameter *testing.T
- Assert expected results
Test Types
- Unit tests
- Table-driven tests
- Benchmarks
- Examples
Test Features
- Parallel testing
- Setup/teardown
- Helper functions
Output
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestOperations
=== RUN TestOperations/add_positive_numbers
=== RUN TestOperations/subtract_positive_numbers
=== RUN TestOperations/multiply_positive_numbers
=== RUN TestOperations/divide_positive_numbers
=== RUN TestOperations/divide_by_zero
--- PASS: TestOperations (0.00s)
=== RUN ExampleAdd
--- PASS: ExampleAdd (0.00s)
=== RUN TestParallelAdd
=== PAUSE TestParallelAdd
=== CONT TestParallelAdd
--- PASS: TestParallelAdd (0.00s)
goos: darwin
goarch: amd64
BenchmarkAdd-8 2000000000 0.33 ns/op
PASS
coverage: 100.0% of statements
Tips
- Tulis test bersamaan dengan kode
- Gunakan table-driven tests
- Manfaatkan parallel testing
- Buat helper functions
- Monitor code coverage