Go'da test yazmak için ek bir kütüphaneye ihtiyacınız yoktur: adı _test.go ile biten bir dosyaya func TestXxx(t *testing.T) imzalı fonksiyonlar yazar, go test komutuyla çalıştırırsınız. Fonksiyon içinde t.Error veya t.Errorf çağrısı testi başarısız sayar ama çalışmayı sürdürür; t.Fatal ise testi o noktada bitirir. Birden çok girdiyi tek fonksiyonda denemek için tablo testleri ve t.Run ile alt testler kullanılır.
Bu yazıda testing paketinin belgesine, go help test ve go help testflag çıktılarına dayanarak gerçek bir paket üzerinde ilerliyoruz: basit test, tablo testi ve alt testler, yardımcı fonksiyonlar için t.Helper, t.Cleanup, t.TempDir ve Go 1.24 ile gelen t.Context, belgede çalışan örnekler (Example fonksiyonları), kara kutu testleri, kapsama ölçümü ve test önbelleği. Tüm çıktılar Go 1.27.3 ile alındı. Performans ölçümü ve fuzzing ayrı bir konu olduğu için Go benchmark ve fuzz testi yazısında ele alınıyor; Go'ya genel bakış için Go öğrenme rehberi sayfasına bakabilirsiniz.
Test edilecek paket
Örneklerin hepsi example.com/hesap modülündeki küçük bir paketi test ediyor. Paket, kuruş cinsinden bir tutara indirim uygulayan, bir başlığı URL dostu hâle getiren ve dışa aktarılmayan bir yuvarlama fonksiyonu içeriyor:
// Package hesap, basit fiyat hesapları yapar.
package hesap
import (
"errors"
"strings"
"unicode"
)
// ErrNegatif, eksi tutar verildiğinde döner.
var ErrNegatif = errors.New("tutar negatif olamaz")
// Indirim, kuruş cinsinden tutara yüzde indirimi uygular.
func Indirim(kurus, yuzde int) (int, error) {
if kurus < 0 {
return 0, ErrNegatif
}
if yuzde > 100 {
yuzde = 100
}
return kurus - kurus*yuzde/100, nil
}
// Slug, başlığı küçük harfe çevirip boşlukları tireyle değiştirir.
func Slug(baslik string) string {
return strings.ReplaceAll(strings.ToLowerSpecial(unicode.TurkishCase, strings.TrimSpace(baslik)), " ", "-")
}
func yuvarla(kurus int) int {
return (kurus + 50) / 100 * 100
}İlk test ve go test çıktısı

testing belgesine göre go test, adı Test ile başlayan ve ardından küçük harf gelmeyen, *testing.T parametreli her fonksiyonu çalıştırır. Test dosyaları normal go build sırasında derlenmez, yalnızca go test çalıştığında pakete eklenir. Test dosyası test ettiği paketle aynı paket adını taşıyorsa dışa aktarılmamış adlara da erişebilir; aşağıda yuvarla fonksiyonunu doğrudan test edebilmemiz bu yüzdendir:
package hesap
import (
"errors"
"testing"
)
func TestIndirim(t *testing.T) {
got, err := Indirim(10000, 15)
if err != nil {
t.Fatalf("beklenmeyen hata: %v", err)
}
if got != 8500 {
t.Errorf("Indirim(10000, 15) = %d; want 8500", got)
}
}
func TestIndirimNegatif(t *testing.T) {
_, err := Indirim(-1, 10)
if !errors.Is(err, ErrNegatif) {
t.Errorf("hata = %v; want ErrNegatif", err)
}
}
func TestYuvarla(t *testing.T) {
if got := yuvarla(1249); got != 1200 {
t.Errorf("yuvarla(1249) = %d; want 1200", got)
}
}$ go test
PASS
ok example.com/hesap 0.022s
$ go test -v
=== RUN TestIndirim
--- PASS: TestIndirim (0.00s)
=== RUN TestIndirimNegatif
--- PASS: TestIndirimNegatif (0.00s)
=== RUN TestYuvarla
--- PASS: TestYuvarla (0.00s)
PASS
ok example.com/hesap 0.021sGeçen paket için go test yalnızca özet satırı yazar; -v her testin adını ve sonucunu gösterir. Süreler her çalıştırmada değişir. Mesajlardaki got ... want ... kalıbı Go topluluğunun ve testing belgesinin kullandığı biçimdir: önce elde edilen, sonra beklenen değer yazılır. Hata kontrolünde == yerine errors.Is kullanmak, hata ileride fmt.Errorf("...: %w", err) ile sarmalansa bile testin doğru kalmasını sağlar (bkz. Go hata yönetimi).
| Metot | Ne yapar | Test devam eder mi? |
|---|---|---|
| t.Log / t.Logf | Mesajı kaydeder; test başarısızsa ya da -v verilmişse görünür | Evet |
| t.Fail | Testi başarısız işaretler | Evet |
| t.Error / t.Errorf | Log ardından Fail | Evet |
| t.FailNow | Başarısız işaretler ve runtime.Goexit ile çıkar | Hayır |
| t.Fatal / t.Fatalf | Log ardından FailNow | Hayır |
| t.Skip / t.Skipf | Log ardından SkipNow; test atlandı sayılır | Hayır |
t.Fatal ailesini, sonraki adımların anlamsız kalacağı durumlarda kullanın; örneğin beklenmeyen bir hata döndüyse dönen değeri karşılaştırmanın anlamı yoktur. Belge önemli bir kısıt da koyar: FailNow, Fatal ve Skip türevleri yalnızca test fonksiyonunu çalıştıran goroutine'den çağrılmalıdır. Testin başlattığı başka bir goroutine'de t.Fatal çağırmak o goroutine'i durdurmaz; go vet içindeki testinggoroutine denetimi bu hatayı arar.
Tablo testleri ve t.Run ile alt testler
Aynı fonksiyonu birçok girdiyle denemenin Go'daki yerleşik yolu, girdileri bir dilimde toplayıp döngüyle dolaşmaktır. Her satırı t.Run ile bir alt teste dönüştürdüğünüzde bir durumun başarısızlığı diğerlerini durdurmaz ve her biri ayrı adla raporlanır. Belgeye göre t.Run, verilen fonksiyonu ayrı bir goroutine'de çalıştırır ve o fonksiyon dönene kadar (ya da t.Parallel çağırana kadar) bekler. Aşağıdaki tablo, ilk sürümde Slug fonksiyonu şöyle yazılmışken çalıştırıldı:
func Slug(baslik string) string {
return strings.ReplaceAll(strings.ToLower(strings.TrimSpace(baslik)), " ", "-")
}package hesap
import "testing"
func TestSlug(t *testing.T) {
tests := []struct {
name string
baslik string
want string
}{
{"basit", "Go Test", "go-test"},
{"bosluklu", " Merhaba Dünya ", "merhaba-dünya"},
{"turkce", "IŞIK Hızı", "ışık-hızı"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Slug(tt.baslik); got != tt.want {
t.Errorf("Slug(%q) = %q; want %q", tt.baslik, got, tt.want)
}
})
}
}$ go test -run TestSlug -v
=== RUN TestSlug
=== RUN TestSlug/basit
=== RUN TestSlug/bosluklu
=== RUN TestSlug/turkce
slug_test.go:18: Slug("IŞIK Hızı") = "işik-hızı"; want "ışık-hızı"
--- FAIL: TestSlug (0.00s)
--- PASS: TestSlug/basit (0.00s)
--- PASS: TestSlug/bosluklu (0.00s)
--- FAIL: TestSlug/turkce (0.00s)
FAIL
exit status 1
FAIL example.com/hesap 0.023sTablo testi gerçek bir hata yakaladı: strings.ToLower Unicode'un genel kurallarını uygular ve büyük I harfini noktalı i yapar. Türkçede doğru sonuç noktasız ı'dır. Paketteki çözüm, yukarıdaki son sürümde görüldüğü gibi strings.ToLowerSpecial(unicode.TurkishCase, ...) kullanmaktır; bu konunun ayrıntısı Go string, rune, byte ve UTF-8 yazısında. Düzeltmeden sonra üç alt test de geçti.
Alt testlerin adı üst testin adına eğik çizgiyle eklenir: TestSlug/turkce. -run bayrağı çapası olmayan bir düzenli ifade alır ve eğik çizgilerle bölünür; her parça adın ilgili düzeyiyle eşleşmelidir. Böylece tek bir durumu çalıştırabilirsiniz:
$ go test -run 'TestSlug/basit' -v
=== RUN TestSlug
=== RUN TestSlug/basit
--- PASS: TestSlug (0.00s)
--- PASS: TestSlug/basit (0.00s)
PASS
ok example.com/hesap 0.024st.Helper, t.Cleanup ve t.TempDir

Karşılaştırma kodunu bir yardımcı fonksiyona taşıdığınızda hata mesajındaki satır numarası yardımcı fonksiyonun içini gösterir, bu da hangi çağrının başarısız olduğunu gizler. t.Helper(), çağıran fonksiyonu yardımcı olarak işaretler ve dosya ile satır bilgisi yazılırken o fonksiyonu atlar. Aşağıdaki testte beklentiyi bilerek yanlış yazdık (200 kuruşa yüzde 50 indirim 100 eder):
package hesap
import (
"os"
"path/filepath"
"testing"
)
func esitMi(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got %d; want %d", got, want)
}
}
func TestYardimci(t *testing.T) {
got, _ := Indirim(200, 50)
esitMi(t, got, 150)
}
func TestGeciciDosya(t *testing.T) {
dir := t.TempDir()
t.Cleanup(func() { t.Log("temizlik 1") })
t.Cleanup(func() { t.Log("temizlik 2") })
yol := filepath.Join(dir, "fiyat.txt")
if err := os.WriteFile(yol, []byte("8500"), 0o644); err != nil {
t.Fatal(err)
}
b, err := os.ReadFile(yol)
if err != nil {
t.Fatal(err)
}
t.Logf("okunan: %s", b)
}$ go test -run TestYardimci
--- FAIL: TestYardimci (0.00s)
yardim_test.go:18: got 100; want 150
FAIL
exit status 1
FAIL example.com/hesap 0.022sHata, çağrının yapıldığı 18. satırı gösteriyor. Aynı testi t.Helper() satırı olmadan çalıştırdığımızda mesaj yardim_test.go:12, yani esitMi içindeki t.Errorf satırı oldu. Aynı dosyadaki ikinci test ise t.TempDir ile t.Cleanup kullanıyor. TempDir her çağrıda benzersiz bir dizin döndürür ve test ile tüm alt testleri bitince dizini otomatik siler. Cleanup ile kaydedilen fonksiyonlar test bitince son eklenen ilk çalışacak şekilde çağrılır:
$ go test -run TestGeciciDosya -v
=== RUN TestGeciciDosya
yardim_test.go:33: okunan: 8500
yardim_test.go:24: temizlik 2
yardim_test.go:23: temizlik 1
--- PASS: TestGeciciDosya (0.00s)
PASS
ok example.com/hesap 0.027sCleanup sırası defer ile aynı mantıktadır (bkz. Go defer, panic ve recover), ama bir farkı vardır: yardımcı fonksiyonun içinden de kaydedilebilir ve yardımcı fonksiyon döndüğünde değil, test bittiğinde çalışır. Bu yüzden "bir sunucu başlat ve test bitince kapat" türü yardımcılar için uygundur.
t.Context: test bitince iptal edilen context
Go 1.24 sürüm notlarına göre T.Context ve B.Context, test tamamlandıktan sonra ve temizlik fonksiyonları çalışmadan hemen önce iptal edilen bir context.Context döndürür. Testte başlattığınız goroutine'lere bu context'i verirseniz, Cleanup içinde onların kapanmasını bekleyebilirsiniz:
package hesap
import (
"testing"
)
func TestContext(t *testing.T) {
ctx := t.Context()
bitti := make(chan struct{})
go func() {
<-ctx.Done()
close(bitti)
}()
t.Cleanup(func() {
<-bitti
t.Log("context, Cleanup'tan önce iptal edildi:", ctx.Err())
})
}
func TestUcYuz(t *testing.T) {
got, _ := Indirim(500, 150)
if got != 0 {
t.Errorf("got %d; want 0", got)
}
}$ go test -run TestContext -v
=== RUN TestContext
ctx_test.go:16: context, Cleanup'tan önce iptal edildi: context canceled
--- PASS: TestContext (0.00s)
PASS
ok example.com/hesap 0.026sContext'in iptal yayılımı ve zaman aşımı desenleri Go context paketi yazısında. go tool fix help listesindeki testingcontext düzelticisi, testlerde elle kurulan context.WithCancel çağrılarını t.Context ile değiştirmeyi önerir.
Example fonksiyonları: belge ve test bir arada
testing paketi örnek kodu da çalıştırıp doğrular. Adı Example ile başlayan bir fonksiyonun sonunda // Output: ile başlayan bir yorum varsa, fonksiyonun standart çıktısı bu yorumla karşılaştırılır; karşılaştırma baştaki ve sondaki boşlukları yok sayar. Satır sırası önemsizse // Unordered output: kullanılır. Çıktı yorumu olmayan örnekler derlenir ama çalıştırılmaz. Adlandırma kuralı belgeyle eşleşmeyi sağlar: ExampleIndirim fonksiyonu Indirim fonksiyonunun, ExampleSepet_Ekle ise Sepet tipinin Ekle metodunun belgesinde görünür. Aynı ad için birden çok örnek gerekirse küçük harfle başlayan bir ek eklenir: ExampleIndirim_sinir.
Aşağıdaki dosya ayrıca kara kutu testinin nasıl yazıldığını da gösteriyor: paket adı hesap_test olduğu için test edilen paket example.com/hesap olarak içe aktarılıyor ve yalnızca dışa aktarılan adlar kullanılabiliyor. testing belgesi bu yaklaşımı "black box" testi olarak adlandırır; paketinizi kullanıcının gördüğü biçimde test etmenizi sağlar.
package hesap_test
import (
"fmt"
"example.com/hesap"
)
func ExampleIndirim() {
fiyat, err := hesap.Indirim(10000, 15)
fmt.Println(fiyat, err)
// Output: 8500 <nil>
}
func ExampleSlug() {
fmt.Println(hesap.Slug("Kış Kampanyası"))
// Output: kış-kampanyası
}$ go test -run Example -v
=== RUN ExampleIndirim
--- PASS: ExampleIndirim (0.00s)
=== RUN ExampleSlug
--- PASS: ExampleSlug (0.00s)
PASS
ok example.com/hesap 0.025sÇıktı yorumunu kis-kampanyasi olarak değiştirdiğimizde go test beklenen ile elde edileni alt alta gösterdi:
--- FAIL: ExampleSlug (0.00s)
got:
kış-kampanyası
want:
kis-kampanyasi
FAIL
exit status 1
FAIL example.com/hesap 0.024sExample fonksiyonlarının asıl değeri, go doc ve pkg.go.dev üzerinde belge olarak görünmeleri ve her test çalıştırmasında doğrulandıkları için güncelliğini yitirmemeleridir. Go 1.27 ile go doc -ex bir paketin örneklerini listeleyebiliyor; ayrıntısı Go araçları yazısında.
go test sırasında çalışan go vet
go help test çıktısına göre go test, test ikilisini oluştururken pakete ve test dosyalarına go vet uygular; sorun bulursa testleri hiç çalıştırmaz. Kullanılan denetimler yüksek güvenilirlikli bir alt kümedir: atomic, bools, buildtag, directive, errorsas, ifaceassert, nilfunc, printf, stdversion, stringintconv ve tests. Bunlardan stdversion, Go 1.27 sürüm notlarına göre artık varsayılan olarak çalışıyor ve go.mod dosyasındaki go sürümü için fazla yeni olan standart kütüphane sembollerini raporluyor. Aşağıdaki testte %d fiiline metin verdik:
package hesap
import "testing"
func TestVet(t *testing.T) {
got, _ := Indirim(100, 10)
t.Logf("sonuç: %d kuruş", "doksan")
_ = got
}# example.com/hesap
# [example.com/hesap]
./vet_test.go:7:18: (*testing.common).Logf format %d has arg "doksan" of wrong type string
FAIL example.com/hesap [build failed]Bu denetimi kapatmak için -vet=off, tüm vet denetimlerini çalıştırmak için -vet=all verilebilir. Biçim fiillerinin listesi Go fmt ve Printf yazısında.
Kapsama (coverage) ölçümü
-cover bayrağı testlerin paketteki deyimlerin yüzde kaçını çalıştırdığını yazar. -coverprofile sonucu bir dosyaya kaydeder ve -cover bayrağını da açar; dosyayı go tool cover -func ile fonksiyon bazında, -html ile tarayıcıda renkli olarak inceleyebilirsiniz. İlk ölçümde yüzde 100'ün altında kaldık:
$ go test -cover
PASS
coverage: 85.7% of statements
ok example.com/hesap 0.028s
$ go test -coverprofile=c.out
PASS
coverage: 85.7% of statements
ok example.com/hesap 0.033s
$ go tool cover -func=c.out
example.com/hesap/hesap.go:14: Indirim 80.0%
example.com/hesap/hesap.go:25: Slug 100.0%
example.com/hesap/hesap.go:29: yuvarla 100.0%
total: (statements) 85.7%Indirim içindeki yuzde > 100 dalı hiçbir testte çalışmamıştı. ctx_test.go dosyasındaki TestUcYuz testi tam bu durumu (yüzde 150 indirim) deniyor; o eklendikten sonra go test -cover çıktısı coverage: 100.0% of statements oldu. Kapsama, hangi kodun hiç denenmediğini gösteren bir araçtır; yüzde 100 olması kodun doğru olduğunu kanıtlamaz. Varsayılan ölçüm kipi set'tir (deyim çalıştı mı?); -covermode=count her deyimin kaç kez çalıştığını sayar.
Test önbelleği ve sık kullanılan bayraklar

go test iki kipte çalışır. Paket argümanı vermeden (go test) çalıştırdığınızda yalnızca bulunduğunuz dizindeki paket test edilir ve önbellek kullanılmaz. Paket listesi verdiğinizde (go test ., go test ./...) başarılı sonuçlar önbelleğe alınır; aynı test ikilisi ve önbelleğe uygun bayraklarla tekrar çalıştırırsanız test yeniden koşmaz, özet satırında süre yerine (cached) yazar:
$ go test .
ok example.com/hesap 0.027s
$ go test .
ok example.com/hesap (cached)
$ go test -count=1 ./...
ok example.com/hesap 0.026sÖnbelleği bilerek atlamanın geleneksel yolu -count=1 vermektir. Testler paket içindeki dosyaları ya da ortam değişkenlerini okuyorsa, önbellek yalnızca bunlar değişmediğinde eşleşir. go aracı testdata adlı dizini yok sayar; test girdilerinizi oraya koyabilirsiniz.
| Bayrak | İşlevi |
|---|---|
| -v | Her testi ve tüm Log çıktısını gösterir |
| -run regexp | Yalnızca eşleşen testleri, örnekleri ve fuzz testlerini çalıştırır |
| -skip regexp | Eşleşenleri atlar |
| -count n | Her testi n kez çalıştırır; -count=1 önbelleği atlar |
| -short | testing.Short() true döner; uzun testler kendini kısaltabilir |
| -failfast | İlk başarısızlıktan sonra yeni test başlatmaz |
| -timeout d | Varsayılan 10 dakika; aşılırsa panic |
| -cover, -coverprofile | Kapsama ölçümü |
| -shuffle on | Test sırasını karıştırır; sıraya bağımlılığı ortaya çıkarır |
| -json | Çıktıyı makinece okunabilir JSON olarak verir |
Uzun süren testleri if testing.Short() { t.Skip("kısa kipte atlanıyor") } ile koşullu hâle getirebilirsiniz. Birbirinden bağımsız testleri t.Parallel() ile paralel çalıştırabilirsiniz; belge, t.Setenv ve t.Chdir gibi tüm süreci etkileyen metotların paralel testlerde kullanılamayacağını belirtir. Paket genelinde kurulum ve yıkım gerekiyorsa func TestMain(m *testing.M) tanımlanabilir, ama belge onu düşük seviyeli bir araç olarak tanımlar ve sıradan ihtiyaçlar için gerekli görmez. Yarış koşullarını yakalamak için go test -race kullanılır; bu konu Go veri yarışı ve sync/atomic yazısında.
Sık yapılan hatalar
- Test fonksiyonunu yanlış adlandırmak:
Testindirimçalışmaz, çünküTestsonrasındaki harf küçük olmamalıdır. Go 1.24 ile eklenen vettestsdenetimi bu tür adlandırma ve imza hatalarını raporlar. - Her yerde t.Fatal kullanmak: İlk hatada durduğu için diğer sorunları görmezsiniz. Sonraki adımlar anlamlıysa
t.Errorftercih edin. - Başka goroutine'den t.Fatal çağırmak: Belgeye göre bu metotlar yalnızca testin kendi goroutine'inden çağrılmalıdır.
- Önbelleğe takılıp testin çalıştığını sanmak:
(cached)görüyorsanız test yeniden koşmadı. Dış kaynaklara bağlı testlerde-count=1kullanın. - Example çıktısında rastgele sıraya güvenmek: Map dolaşımı gibi sırası belirsiz çıktılar için
Unordered outputkullanın ya da çıktıyı sıralayın. - Testleri sıraya bağımlı yazmak: Bir testin bıraktığı durumu diğeri kullanıyorsa
-shuffle onile çalıştırıp sorunu erken görün.
Sık Sorulan Sorular
Test dosyası aynı pakette mi olmalı, _test paketinde mi?
İkisi de geçerlidir ve aynı dizinde birlikte bulunabilir. Aynı paket (beyaz kutu) dışa aktarılmamış fonksiyonları doğrudan test etmenizi sağlar; paketadi_test (kara kutu) ise paketi kullanıcılarınızın göreceği API üzerinden sınar. Örnek fonksiyonları için kara kutu paketi daha doğal sonuç verir.
Go'da assert kütüphanesi kullanmam gerekir mi?
testing paketi karşılaştırma yardımcıları sunmaz; belgedeki ve standart kütüphanedeki testler düz if ve t.Errorf ile yazılır. Ortak karşılaştırmaları kendi t.Helper işaretli fonksiyonlarınızda toplayabilirsiniz. Üçüncü taraf kütüphaneler bu yazının kapsamı dışında.
Yalnızca tek bir testi nasıl çalıştırırım?
go test -run '^TestIndirim$' yazın. -run değeri çapası olmayan bir düzenli ifade olduğu için ^ ve $ olmadan TestIndirim, TestIndirimNegatif ile de eşleşir. Alt test için eğik çizgi kullanın: -run 'TestSlug/turkce'.
HTTP işleyicilerini nasıl test ederim?
net/http/httptest paketinin NewRecorder ve NewServer fonksiyonlarıyla, gerçek bir port açmadan. Örnekler Go net/http web sunucusu ve Go HTTP istemcisi yazılarında.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Go öğrenme rehberi · Go benchmark ve fuzz testi · Go paket yapısı · gofmt, go vet ve go doc · Go hata yönetimi · GitHub Actions CI/CD rehberi