1. Birim Testine Giriş
Birim testi, programdaki en küçük test edilebilir birimi, örneğin bir fonksiyon veya bir methodu Go dilinde kontrol etmek ve doğrulamak anlamına gelir. Birim testi, kodun beklenildiği gibi çalıştığını sağlar ve geliştiricilere mevcut işlevselliği yanlışlıkla bozmadan kod üzerinde değişiklik yapma imkanı tanır.
Golang projesinde birim testinin önemi inkar edilemez. İlk olarak, kod kalitesini arttırabilir ve geliştiricilere kod üzerinde değişiklik yapma konusunda daha fazla güven verir. İkinci olarak, birim testi, kod için belgeler olarak hizmet edebilir ve beklenen davranışını açıklar. Ayrıca, birim testlerini sürekli entegrasyon ortamında otomatik olarak çalıştırmak, yeni hataları hızlı bir şekilde keşfederek yazılımın kararlılığını arttırabilir.
2. testing
Paketi Kullanarak Temel Testler Yapma
Go dilinin standart kütüphanesi, testlerin yazılması ve çalıştırılması için araçlar ve işlevsellik sağlayan testing
paketini içerir.
2.1 İlk Test Durumunuzu Oluşturma
Bir test fonksiyonu yazmak için, _test.go
uzantılı bir dosya oluşturmanız gerekir. Örneğin, kaynak kod dosyanızın adı calculator.go
ise, test dosyanızın adı calculator_test.go
olmalıdır.
Sonrasında test fonksiyonunu oluşturma zamanı geldi. Bir test fonksiyonu, testing
paketini içe aktarmalı ve belirli bir formata uymalıdır. İşte basit bir örnek:
// calculator_test.go
package calculator
import (
"testing"
"fmt"
)
// Toplama işlemini test et
func TestAdd(t *testing.T) {
result := Add(1, 2)
expected := 3
if result != expected {
t.Errorf("Beklenen %v, ancak elde edilen %v", expected, result)
}
}
Bu örnekte, TestAdd
, hayali bir Add
fonksiyonunu test eden bir test fonksiyonudur. Eğer Add
fonksiyonunun sonucu beklenen sonuçla eşleşirse, test başarılı olacak; aksi takdirde t.Errorf
çağrılacaktır ve test başarısız olacaktır.
2.2 Test Fonksiyonlarının Adlandırma Kurallarını ve İmzasını Anlama
Test fonksiyonları, Test
ile başlamalıdır, ardından küçük harf olmayan herhangi bir dize gelmelidir ve sadece parametreleri, testing.T
nin bir işaretçisi olmalıdır. Örnekte görüldüğü gibi, TestAdd
doğru adlandırma kurallarını ve imzasını takip eder.
2.3 Test Durumlarının Çalıştırılması
Test durumlarınızı komut satırı aracılığıyla çalıştırabilirsiniz. Belirli bir test durumu için aşağıdaki komutu çalıştırın:
go test -v // Mevcut dizindeki testleri çalıştır ve detaylı çıktıyı görüntüle
Belirli bir test durumunu çalıştırmak istiyorsanız, -run
bayrağını bir düzenli ifade ile kullanabilirsiniz:
go test -v -run TestAdd // Yalnızca TestAdd test fonksiyonunu çalıştır
go test
komutu otomatik olarak tüm _test.go
dosyalarını bulacak ve kriterleri karşılayan her test fonksiyonunu çalıştıracaktır. Eğer tüm testler başarılı olursa, komut satırında PASS
benzeri bir mesaj göreceksiniz; eğer herhangi bir test başarısız olursa, FAIL
ile birlikte ilgili hata mesajını göreceksiniz.
3. Test Durumlarının Yazılması
3.1 t.Errorf
ve t.Fatalf
Kullanarak Hataları Bildirme
Go dilinde, test çerçevesi hataları bildirmek için çeşitli yöntemler sağlar. En yaygın kullanılan fonksiyonlar Errorf
ve Fatalf
'tir, her ikisi de testing.T
nesnesinin yöntemleridir. Errorf
, testte hataları bildirmek için kullanılır ancak mevcut test durumunu durdurmaz, Fatalf
ise mevcut testi hemen durdurur. Test gereksinimlerine bağlı olarak uygun yöntemi seçmek önemlidir.
Errorf
kullanım örneği:
func TestAdd(t *testing.T) {
got := Add(1, 2)
want := 3
if got != want {
t.Errorf("Add(1, 2) = %d; want %d", got, want)
}
}
Eğer bir hatayı tespit ettiğinizde testi hemen durdurmak istiyorsanız, Fatalf
'ı kullanabilirsiniz:
func TestSubtract(t *testing.T) {
got := Subtract(5, 3)
if got != 2 {
t.Fatalf("Subtract(5, 3) = %d; want 2", got)
}
}
Genel olarak, hata sonrasında sonraki kodun doğru bir şekilde çalışmasına engel olacaksa veya test başarısızlığı önceden doğrulanabiliyorsa, Fatalf
'in kullanılması önerilir. Aksi takdirde, daha kapsamlı bir test sonucu elde etmek için Errorf
kullanılması önerilir.
3.2 Alt Testleri Düzenleme ve Alt Testleri Çalıştırma
Go'da alt testleri düzenlemek için t.Run
kullanabiliriz, bu da test kodunu daha yapılandırılmış bir şekilde yazmamıza yardımcı olur. Alt testler kendi Kurulum
ve Son Temizlik
işlevlerine sahip olabilir ve tek tek çalıştırılabilir, bu da harika bir esneklik sağlar. Bu özellik özellikle karmaşık testlerin veya parametreli testlerin yapılması için çok faydalıdır.
Alt test t.Run
kullanım örneği:
func TestCarpma(t *testing.T) {
testcases := []struct {
name string
a, b, expected int
}{
{"2x3", 2, 3, 6},
{"-1x-1", -1, -1, 1},
{"0x4", 0, 4, 0},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
if got := Carpma(tc.a, tc.b); got != tc.expected {
t.Errorf("Carpma(%d, %d) = %d; beklenen %d", tc.a, tc.b, got, tc.expected)
}
})
}
}
Eğer "2x3" adındaki alt testi tek başına çalıştırmak istiyorsak, aşağıdaki komutu komut satırında çalıştırabiliriz:
go test -run TestCarpma/2x3
Lütfen alt test adlarının harf duyarlı olduğunu unutmayın.
4. Testten Önce ve Sonra Hazırlık Çalışmaları
4.1 Kurulum ve Son Temizlik
Testler yaparken genellikle testler için bazı başlangıç durumlarını hazırlamamız gerekir (örneğin, veritabanı bağlantısı, dosya oluşturma vb.), ve benzer şekilde, testler tamamlandıktan sonra bazı temizlik çalışmaları yapmamız gerekir. Go'da genellikle Kurulum
ve Son Temizlik
'i doğrudan test fonksiyonlarında gerçekleştiririz ve t.Cleanup
işlevi, temizlik geri çağrı işlevlerini kaydetme yeteneği sağlar.
İşte basit bir örnek:
func TestVeritabani(t *testing.T) {
db, err := VeritabaniKurulumu()
if err != nil {
t.Fatalf("kurulum başarısız: %v", err)
}
// Test tamamlandığında veritabanı bağlantısının kapatılmasını sağlamak için bir temizlik geri çağrısı kaydet
t.Cleanup(func() {
if err := db.Kapat(); err != nil {
t.Errorf("veritabanı kapatılamadı: %v", err)
}
})
// Testi gerçekleştir...
}
TestVeritabani
fonksiyonunda, önce test ortamını kurmak için VeritabaniKurulumu
fonksiyonunu çağırıyoruz. Sonra, t.Cleanup()
'ı kullanarak test tamamlandıktan sonra temizlik çalışmalarını gerçekleştirmek üzere bir fonksiyon kaydediyoruz, bu örnekte, veritabanı bağlantısını kapatmak. Bu şekilde, test başarılı olup olmasın, kaynakların doğru bir şekilde serbest bırakıldığından emin olabiliriz.
5. Test Verimliliğini Artırma
Test verimliliğini artırmak, geliştirmeyi daha hızlı döngüye almak, problemleri hızlıca bulmak ve kod kalitesini sağlamak için önemlidir. Aşağıda, test kapsamı, tablo odaklı testler ve test verimliliğini artırmak için mock kullanımı konusunu tartışacağız.
5.1 Test Kapsamı ve İlgili Araçlar
go test
aracı, bize test durumlarının hangi kısımlarını kapsadığını anlamamıza yardımcı olan oldukça faydalı bir test kapsamı özelliği sağlar, bu da test durumları tarafından kapsanmayan kod alanlarını keşfetmemizi sağlar.
go test -cover
komutunu kullanarak mevcut test kapsamı yüzdesini görebiliriz:
go test -cover
Hangi satırların çalıştırıldığını ve hangilerinin çalıştırılmadığını daha detaylı anlamak istiyorsanız, kapsama veri dosyası oluşturan -coverprofile
parametresini kullanabilirsiniz. Ardından, detaylı bir test kapsamı raporu oluşturmak için go tool cover
komutunu kullanabilirsiniz.
go test -coverprofile=kapsama.out
go tool cover -html=kapsama.out
Yukarıdaki komut, web tabanlı bir rapor açacak ve hangi kod satırlarının test edildiğini hangilerinin test edilmediğini görsel olarak gösterecektir. Yeşil, test edilmiş kodu temsil ederken, kırmızı test edilmemiş kod satırlarını temsil eder.
5.2 Mock Kullanımı
Test etme sürecinde genellikle dış bağımlılıkları taklit etmemiz gereken durumlarla karşılaşırız. Mock'lar, bu bağımlılıkları taklit ederek test ortamında belirli dış servislere veya kaynaklara bağlı kalmaktan kaçınmamıza yardımcı olabilir.
Go topluluğunda testify/mock
ve gomock
gibi birçok mock aracı bulunmaktadır. Bu araçlar genellikle mock objeler oluşturmak ve kullanmak için bir dizi API sağlar.
İşte testify/mock
'un temel kullanımına ilişkin bir örnek. İlk yapılması gereken şey, bir arayüz ve onun mock versiyonunu tanımlamaktır:
type DataService interface {
FetchData() (int, error)
}
type MockDataService struct {
mock.Mock
}
func (m *MockDataService) FetchData() (int, error) {
args := m.Called()
return args.Int(0), args.Error(1)
}
Test etme sürecinde, MockDataService
'yi gerçek veri servisinin yerine kullanabiliriz:
func TestSomething(t *testing.T) {
mockDataSvc := new(MockDataService)
mockDataSvc.On("FetchData").Return(42, nil) // Beklenen davranışı yapılandırma
result, err := mockDataSvc.FetchData() // Mock objeyi kullanma
assert.NoError(t, err)
assert.Equal(t, 42, result)
mockDataSvc.AssertExpectations(t) // Beklenen davranışın gerçekleşip gerçekleşmediğini doğrulama
}
Yukarıdaki yöntemle, test etmenin dış servislere, veritabanı çağrılarına vb. bağlı kalmaktan kaçınabilir ve bu testin daha hızlı çalışmasını sağlayabiliriz. Bu, testlerimizi daha istikrarlı ve güvenilir hale getirebilir.
6. Gelişmiş Test Teknikleri
Go birim testinin temellerini öğrendikten sonra, test etme verimliliğini artırmaya ve daha sağlam yazılımlar oluşturmaya yardımcı olan bazı daha gelişmiş test tekniklerini keşfedebiliriz.
6.1 Özel Fonksiyonların Test Edilmesi
Golang'da, özel fonksiyonlar genellikle erişimi olmayan fonksiyonları ifade eder, yani adı küçük harfle başlayan fonksiyonlar. Genellikle kodun kullanılabilirliğini yansıttığı için genellikle genel arayüzleri test etmeyi tercih ederiz. Ancak özel fonksiyonları doğrudan test etmenin de anlamı olduğu durumlar vardır; örneğin, özel fonksiyonun karmaşık mantığı varsa ve birden fazla genel fonksiyon tarafından çağrılıyorsa.
Özel fonksiyonların test edilmesi, dış paketten erişilemeyeceği için genel fonksiyonları test etmekten farklıdır. Yaygın bir teknik, aynı pakete test kodunu yazmak ve bu şekilde özel fonksiyona erişime izin vermektir.
İşte basit bir örnek:
// calculator.go
package calculator
func add(a, b int) int {
return a + b
}
Buna karşılık gelen test dosyası aşağıdaki gibidir:
// calculator_test.go
package calculator
import "testing"
func TestAdd(t *testing.T) {
expected := 4
actual := add(2, 2)
if actual != expected {
t.Errorf("beklenen %d, alınan %d", expected, actual)
}
}
Test dosyasını aynı pakete yerleştirerek add
fonksiyonunu doğrudan test edebiliriz.
6.2 Ortak Test Kalıpları ve En İyi Uygulamalar
Golang birim testi, test işini kolaylaştıran ve kodun netliğini ve sürdürülebilirliğini korumaya yardımcı olan bazı ortak kalıplara sahiptir.
-
Tablo Tabanlı Testler
Tablo tabanlı test, test girdilerini ve beklenen çıktılarını düzenlemenin bir yöntemidir. Bir dizi test durumu tanımlayarak bunları döngü ile test etmek, bu yöntemi yeni test durumları eklemeyi çok kolay hale getirir ve kodu okunaklı ve sürdürülebilir hale getirir.
// calculator_test.go
package calculator
import "testing"
func TestAddTableDriven(t *testing.T) {
var tests = []struct {
a, b int
want int
}{
{1, 2, 3},
{2, 2, 4},
{5, -1, 4},
}
for _, tt := range tests {
testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
t.Run(testname, func(t *testing.T) {
ans := add(tt.a, tt.b)
if tt.want != ans {
t.Errorf("alınan %d, beklenen %d", ans, tt.want)
}
})
}
}
-
Mock'ların Kullanımı için Testler
Mock'lar, çeşitli işlevlerin test edilmesi için bağımlılıkların yerine geçme tekniğidir. Golang'da, mock'ları uygulamanın temel yolu arayüzlerdir. Arayüzleri kullanarak, mock bir uygulama oluşturulabilir ve ardından testlerde kullanılabilir.