Testing

Testing adalah bagian penting dari pengembangan software. Go menyediakan package testing untuk unit testing, benchmark, dan example testing.

Contoh Masalah

Bagaimana cara:

  1. Menulis unit test
  2. Melakukan benchmark
  3. 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

  1. Basic Testing

    • Nama fungsi TestXxx
    • Parameter *testing.T
    • Assert expected results
  2. Test Types

    • Unit tests
    • Table-driven tests
    • Benchmarks
    • Examples
  3. 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