لغة Go بالأمثلة: الاختبار وقياس الأداء

اختبار الوحدات جزء مهم من كتابة برامج Go سليمة الأسس. توفر الحزمة testing الأدوات التي نحتاجها لكتابة اختبارات الوحدات، ويشغّل الأمر go test الاختبارات.

لأغراض التوضيح، يوجد هذا الكود في الحزمة main، لكنه قد يوجد في أي حزمة. يوجد كود الاختبار عادة في الحزمة نفسها التي يوجد فيها الكود الذي يختبره.

package main
import (
    "fmt"
    "testing"
)

سنختبر هذا التنفيذ البسيط لإيجاد أصغر عدد صحيح. عادة ما يكون الكود الذي نختبره في ملف مصدري باسم مثل intutils.go، ويكون ملف اختباره حينئذ باسم intutils_test.go.

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

يُنشأ الاختبار بكتابة دالة يبدأ اسمها بـTest.

func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {

تبلغ t.Error* عن إخفاقات الاختبار، لكنها تواصل تنفيذ الاختبار. تبلغ t.Fatal* عن الإخفاقات وتوقف الاختبار فورًا.

        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

قد تكون كتابة الاختبارات متكررة، لذا من الشائع استخدام *أسلوب قائم على جدول*، تُسرد فيه مدخلات الاختبار ومخرجاته المتوقعة في جدول، وتجتازها حلقة واحدة لتنفيذ منطق الاختبار.

func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

تتيح t.Run تشغيل «اختبارات فرعية»، اختبارًا لكل صف في الجدول. تُعرض هذه الاختبارات منفصلة عند تنفيذ go test -v.

    for _, tt := range tests {
        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

توجد اختبارات قياس الأداء عادة في ملفات _test.go، وتبدأ أسماؤها بـBenchmark. يوضع قبل هذه الحلقة أي كود يلزم لتشغيل قياس الأداء، لكن ينبغي ألا يدخل ضمن القياس.

func BenchmarkIntMin(b *testing.B) {
    for b.Loop() {

سينفذ مشغّل قياس الأداء متن هذه الحلقة مرات كثيرة تلقائيًا لتحديد تقدير معقول لوقت تشغيل تكرار واحد.

        IntMin(1, 2)
    }
}

شغّل جميع الاختبارات في المشروع الحالي بالوضع التفصيلي.

$ go test -v
== RUN   TestIntMinBasic
--- PASS: TestIntMinBasic (0.00s)
=== RUN   TestIntMinTableDriven
=== RUN   TestIntMinTableDriven/0,1
=== RUN   TestIntMinTableDriven/1,0
=== RUN   TestIntMinTableDriven/2,-2
=== RUN   TestIntMinTableDriven/0,-1
=== RUN   TestIntMinTableDriven/-1,0
--- PASS: TestIntMinTableDriven (0.00s)
    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
PASS
ok      examples/testing-and-benchmarking    0.023s

شغّل جميع قياسات الأداء في المشروع الحالي. تُشغّل الاختبارات كلها قبل قياسات الأداء. يرشح الخيار bench أسماء دوال قياس الأداء باستخدام تعبير نمطي.

$ go test -bench=.
goos: darwin
goarch: arm64
pkg: examples/testing
BenchmarkIntMin-8 1000000000 0.3136 ns/op
PASS
ok      examples/testing-and-benchmarking    0.351s

المثال التالي: .