68 / 68

testing

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.

example.go
import "testing"

1. Test faylining tuzilishi va nomlash qoidalari

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.

example.go
// math.go
package mathutil

func Add(a, b int) int {
    return a + b
}
example.go
// 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:

bash
go test -v

Natija:

kod
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      mathutil        0.826s

[!IMPORTANT] go test funksiyani test sifatida tanishi uchun uchta qoida bajarilishi shart: (1) fayl nomi _test.go bilan tugashi, (2) funksiya nomi Test bilan boshlanishi (masalan, TestAdd, kichik t bilan Testadd emas!), (3) funksiya yagona *testing.T parametrini qabul qilib, hech narsa qaytarmasligi kerak. Shartlardan birortasi buzilsa, go test bu funksiyani hech qanday ogohlantirishsiz jimgina o'tkazib yuboradi.


2. Muvaffaqiyatsizlikni xabar qilish: Errorf() va Fatalf()

*testing.T muvaffaqiyatsizlikni bildirish uchun ikkita asosiy usul beradi, va ular orasidagi farq muhim:

example.go
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
    }
}
MetodTest davom etadimi?Qachon ishlatiladi
t.Error() / t.Errorf()HaBir nechta mustaqil tekshiruvni birin-ketin ko'rsatish uchun
t.Fatal() / t.Fatalf()Yo'qKeyingi 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.


3. Bir nechta holatni tekshirish: Table-Driven Testlar

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:

example.go
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:

kod
=== 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.


4. Nima uchun Goda "assertion kutubxonalari" yo'q?

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:

example.go
// 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.


5. Kodning tezligini o'lchash: Benchmarklar

Test qatorida, funksiya nomi Benchmark bilan boshlansa va *testing.B qabul qilsa, u oddiy test emas, benchmark (tezlikni o'lchash) hisoblanadi:

example.go
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:

bash
go test -bench=.

Natija:

kod
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.


Xulosa: qachon qaysi vosita kerak?

VazifaVosita
Oddiy testni e'lon qilishfunc TestNomi(t *testing.T)
Muvaffaqiyatsizlikni qayd etib davom etisht.Errorf()
Muvaffaqiyatsizlikni qayd etib darhol to'xtasht.Fatalf()
Bir nechta holatni tartibli tekshirishTable-driven test + t.Run()
Kod tezligini o'lchashfunc BenchmarkNomi(b *testing.B) + go test -bench=.
Barcha testlarni ishga tushirishgo test ./...
Har bir test nomini alohida ko'rsatib ishga tushirishgo 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.