testing paketi
Ko'pgina dasturlash tillarida test yozish uchun alohida tashqi kutubxona (framework) o'rnatish kerak bo'ladi. Go bunday emas — testing paketi standart kutubxonaning o'zida keladi, va test ishga tushirish (go test buyrug'i) kompilyatorning o'ziga o'rnatilgan.
import "testing"
Test kodi asosiy kod bilan bir xil papkada, lekin nomi _test.go bilan tugaydigan faylda yoziladi. Oddiy go build bu fayllarni butunlay e'tiborsiz qoldiradi — ular faqat go test buyrug'i ishga tushirilganda ishlatiladi.
// math.go
package mathutil
func Add(a, b int) int {
return a + b
}
// math_test.go
package mathutil
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; kutilgan 5", result)
}
}
Terminalda ishga tushirish:
go test -v
Natija:
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok mathutil 0.826s
[!IMPORTANT]
go testfunksiyani test sifatida tanishi uchun uchta qoida bajarilishi shart: (1) fayl nomi_test.gobilan tugashi, (2) funksiya nomiTestbilan boshlanishi (masalan,TestAdd, kichiktbilanTestaddemas!), (3) funksiya yagona*testing.Tparametrini qabul qilib, hech narsa qaytarmasligi kerak. Shartlardan birortasi buzilsa,go testbu funksiyani hech qanday ogohlantirishsiz jimgina o'tkazib yuboradi.
Errorf() va Fatalf()*testing.T muvaffaqiyatsizlikni bildirish uchun ikkita asosiy usul beradi, va ular orasidagi farq muhim:
func TestExample(t *testing.T) {
got := 4
want := 5
if got != want {
t.Errorf("got %d, want %d", got, want) // xatoni qayd etadi, TEST DAVOM ETADI
}
// ...
err := someOperation()
if err != nil {
t.Fatalf("kutilmagan xatolik: %v", err) // xatoni qayd etadi va TESTNI DARHOL TO'XTATADI
}
}
| Metod | Test davom etadimi? | Qachon ishlatiladi |
|---|---|---|
t.Error() / t.Errorf() | Ha | Bir nechta mustaqil tekshiruvni birin-ketin ko'rsatish uchun |
t.Fatal() / t.Fatalf() | Yo'q | Keyingi tekshiruvlar mantiqsiz bo'lib qoladigan jiddiy holatlarda (masalan, kutilmagan nil) |
[!WARNING]
t.Fatal()ni faqat testning o'z goroutine'idan chaqiring. Uni boshqa (yordamchi) goroutine'dan chaqirish testni emas, o'sha yordamchi goroutine'ning o'zini to'xtatadi — bu esa test "osilib qolishi" yoki kutilmagan natija berishiga olib kelishi mumkin.
Go testlashida eng ko'p qo'llaniladigan uslub — table-driven test: har bir holat uchun alohida test funksiyasi yozish o'rniga, holatlar ro'yxati (jadval) ustida aylanadigan bitta test funksiyasi yoziladi:
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"musbat sonlar", 2, 3, 5},
{"manfiy sonlar", -2, -3, -5},
{"nol bilan", 0, 5, 5},
}
for _, tt := range tests {
// t.Run() har bir holatni ALOHIDA subtest sifatida ishga tushiradi
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d; kutilgan %d", tt.a, tt.b, got, tt.want)
}
})
}
}
Natija:
=== RUN TestAddTable
=== RUN TestAddTable/musbat_sonlar
=== RUN TestAddTable/manfiy_sonlar
=== RUN TestAddTable/nol_bilan
--- PASS: TestAddTable (0.00s)
--- PASS: TestAddTable/musbat_sonlar (0.00s)
--- PASS: TestAddTable/manfiy_sonlar (0.00s)
--- PASS: TestAddTable/nol_bilan (0.00s)
PASS
[!NOTE]
t.Run()orqali yaratilgan har bir "subtest" mustaqil ishlaydi: agar bittasi muvaffaqiyatsiz bo'lsa, boshqalari baribir davom etadi va natijada aynan qaysi holat(lar) muvaffaqiyatsiz bo'lganini aniq ko'rsatadi.
Ko'p tillarda test yozishda assert.Equal(got, want) kabi tayyor "tasdiqlash" funksiyalaridan foydalaniladi. Go standart kutubxonasida bunday narsa yo'q — bu unutish emas, ongli dizayn qarori:
// Goda bu:
if got != want {
t.Errorf("got %v, want %v", got, want)
}
// boshqa tillardagi kabi bunday emas:
// assert.Equal(t, want, got)
Oddiy if yozish uch qatorga tushadi, lekin muvaffaqiyatsizlik xabari nima deyishi ustidan to'liq nazorat beradi — bu esa test muvaffaqiyatsiz bo'lganda nima noto'g'ri ketganini tezda tushunish imkonini beradi.
Test qatorida, funksiya nomi Benchmark bilan boshlansa va *testing.B qabul qilsa, u oddiy test emas, benchmark (tezlikni o'lchash) hisoblanadi:
func BenchmarkAdd(b *testing.B) {
// b.N — Go avtomatik ravishda ishonchli natija olish uchun tanlaydigan takrorlar soni
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
Ishga tushirish:
go test -bench=.
Natija:
BenchmarkAdd-11 100000000 0.51 ns/op
PASS
ok mathutil 0.325s
Bu natija Add(2, 3) funksiyasini bitta chaqirish o'rtacha 0.51 nanosekund vaqt olishini bildiradi — bunday o'lchovlar, ayniqsa, ikkita xil yechimdan qaysi biri tezroq ekanligini aniq raqamlar bilan solishtirishda juda foydali.
| Vazifa | Vosita |
|---|---|
| Oddiy testni e'lon qilish | func TestNomi(t *testing.T) |
| Muvaffaqiyatsizlikni qayd etib davom etish | t.Errorf() |
| Muvaffaqiyatsizlikni qayd etib darhol to'xtash | t.Fatalf() |
| Bir nechta holatni tartibli tekshirish | Table-driven test + t.Run() |
| Kod tezligini o'lchash | func BenchmarkNomi(b *testing.B) + go test -bench=. |
| Barcha testlarni ishga tushirish | go test ./... |
| Har bir test nomini alohida ko'rsatib ishga tushirish | go test -v |
[!NOTE] Bu darsda test yozishning standart kutubxona orqali asosini ko'rdik. Test yozish falsafasi, mock'lash, integratsiya testlari va coverage haqida chuqurroq ma'lumot uchun Blog bo'limidagi "Testlash asoslari", "Testlarni tashkil qilish va coverage" hamda "Ilg'or testlash" darslariga murojaat qiling.