Go’da Test Yazma

Go, test yazmayı kolaylaşturan yerleşik bir testing paketi ile birlikte gelir. Bu yazıda unit test ve tablo testlerini inceleyeceğiz.

Temel Test Yapısı

Test dosyaları _test.go ile bitmelidir:

// matematik.go
package main

func Topla(a, b int) int {
    return a + b
}

func Carp(a, b int) int {
    return a * b
}
// matematik_test.go
package main

import "testing"

func TestTopla(t *testing.T) {
    sonuc := Topla(2, 3)
    beklenen := 5

    if sonuc != beklenen {
        t.Errorf("Topla(2, 3) = %d; istenilen %d", sonuc, beklenen)
    }
}

func TestCarp(t *testing.T) {
    sonuc := Carp(3, 4)
    beklenen := 12

    if sonuc != beklenen {
        t.Errorf("Carp(3, 4) = %d; istenilen %d", sonuc, beklenen)
    }
}

Tablo Testleri

Aynı fonksiyonu farklı girdilerle test etmek için tablo testleri kullanın:

func TestToplaTablo(t *testing.T) {
    testler := []struct {
        a        int
        b        int
        beklenen int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
        {100, 200, 300},
        {-5, -3, -8},
    }

    for _, tt := range testler {
        t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
            sonuc := Topla(tt.a, tt.b)
            if sonuc != tt.beklenen {
                t.Errorf("Topla(%d, %d) = %d; istenilen %d",
                    tt.a, tt.b, sonuc, tt.beklenen)
            }
        })
    }
}

Test Yardımcıları

func TestToplaHelper(t *testing.T) {
    testler := []struct {
        a, b, beklenen int
    }{
        {1, 2, 3},
        {5, 5, 10},
    }

    for _, tt := range testler {
        // t.Helper() çağıran fonksiyonu raporlar
        if !assertEqual(t, Topla(tt.a, tt.b), tt.beklenen) {
            return
        }
    }
}

func assertEqual(t *testing.T, got, want int) bool {
    t.Helper()
    if got != want {
        t.Errorf("beklenen %d, alınan %d", want, got)
        return false
    }
    return true
}

Hata Testleri

Hata durumlarını test etmek için:

func TestBolmeHata(t *testing.T) {
    _, err := Bol(10, 0)
    if err == nil {
        t.Error("Sıfıra bölme hata döndürmeli")
    }
}

Benchmark Testleri

Performans testi için:

func BenchmarkTopla(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Topla(2, 3)
    }
}

// Benchmark çalıştırma
// go test -bench=.

Testleri Çalıştırma

# Tüm testleri çalıştır
go test

# Detaylı çıktı
go test -v

# Belirli bir testi çalıştır
go test -run TestTopla

# Coverage raporu
go test -cover

# Benchmark
go test -bench=.

# Race condition kontrolü
go test -race

Test Best Practices

  1. Açık isimlendirme: TestFuncName_Input_ExpectedOutput
  2. Table-driven tests: Çoklu senaryolar için
  3. Subtests: İlgili testleri gruplayın
  4. Helper functions: Tekrar eden kodu azaltın
  5. Setup/Teardown: TestMain kullanın
func TestMain(m *testing.M) {
    setup()
    kod := m.Run()
    teardown()
    os.Exit(kod)
}

Mock ile Test

// Interface tanımı
type Database interface {
    GetUser(id int) (*User, error)
}

// Mock implementation
type MockDB struct {
    mockGetUser func(id int) (*User, error)
}

func (m *MockDB) GetUser(id int) (*User, error) {
    return m.mockGetUser(id)
}

// Test
func TestGetUser(t *testing.T) {
    mockDB := &MockDB{
        mockGetUser: func(id int) (*User, error) {
            return &User{Name: "Test"}, nil
        },
    }

    user, err := mockDB.GetUser(1)
    if err != nil {
        t.Fatal(err)
    }

    if user.Name != "Test" {
        t.Errorf("İsim 'Test' olmalı")
    }
}