مقدمة: لماذا تعتبر الاختبارات في Go ضرورة؟
\nGo (Golang) هي لغة برمجة حديثة صُممت لبناء أنظمة موثوقة وقابلة للتوسع. ومع ذلك، حتى الكود الأكثر أناقة ليس محصنًا ضد الأخطاء. ولهذا السبب، تعتبر الاختبارات جزءًا لا يتجزأ من التطوير في Go. توفر الحزمة المدمجة testing مجموعة أدوات قوية لكتابة اختبارات الوحدة، واختبارات التكامل، وحتى اختبارات end-to-end (e2e).
في هذه المقالة، سنستعرض جميع مستويات الاختبارات في Go: من اختبارات الوحدة البسيطة إلى سيناريوهات e2e المعقدة. ستتعلم كيفية هيكلة الاختبارات بشكل صحيح، واستخدام الموكات (mocks)، وكتابة اختبارات table-driven، وإعداد اختبارات التكامل مع التبعيات الحقيقية. ستكون المادة مفيدة لكل من المبتدئين والمطورين ذوي الخبرة الذين يرغبون في تنظيم معرفتهم.
\n\n1. اختبارات الوحدة في Go: الأساسيات
\nتتحقق اختبارات الوحدة من عمل الدوال أو الطرق الفردية بمعزل عن التبعيات الخارجية. في Go، تُستخدم الحزمة القياسية testing لهذا الغرض. القواعد الأساسية: يجب تسمية ملفات الاختبار *_test.go، ويجب أن تبدأ دوال الاختبار بـ Test.
1.1. اختبار وحدة بسيط
\nلننظر إلى دالة جمع رقمين:
\n// math.go\npackage math\n\nfunc Add(a, b int) int {\n return a + b\n}\nالاختبار الخاص بها:
\n// math_test.go\npackage math\n\nimport "testing"\n\nfunc TestAdd(t *testing.T) {\n result := Add(2, 3)\n expected := 5\n if result != expected {\n t.Errorf("Add(2, 3) = %d; want %d", result, expected)\n }\n}\nتشغيل الاختبارات: go test ./... أو go test -v للحصول على إخراج مفصل.
1.2. اختبارات Table-driven (اختبارات جدولية)
\nيستخدم مجتمع Go بشكل نشط نهج table-driven للتحقق من حالات متعددة:
\nfunc TestAddTableDriven(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n want int\n }{\n {"positive", 2, 3, 5},\n {"negative", -1, -2, -3},\n {"zero", 0, 0, 0},\n }\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n if got := Add(tt.a, tt.b); got != tt.want {\n t.Errorf("Add() = %d, want %d", got, tt.want)\n }\n })\n }\n}\nيجعل هذا النهج الاختبارات سهلة القراءة وقابلة للتوسع.
\n\n1.3. اختبار الأخطاء
\nبالنسبة للدوال التي تُرجع أخطاء، استخدم t.Fatal أو t.Errorf:
func TestDivide(t *testing.T) {\n _, err := Divide(10, 0)\n if err == nil {\n t.Fatal("expected error for division by zero")\n }\n}\n\n2. اختبارات التكامل: العمل مع التبعيات
\nتتحقق اختبارات التكامل من تفاعل المكونات: قواعد البيانات، واجهات API، نظام الملفات. في Go، يتم فصلها عادةً عن اختبارات الوحدة باستخدام build tags أو هيكل حزم منفصل.
\n\n2.1. استخدام build tags
\nأنشئ ملفًا مع الوسم //go:build integration في البداية:
//go:build integration\n\npackage db\n\nimport (\n "testing"\n "database/sql"\n _ "github.com/lib/pq"\n)\n\nfunc TestPostgresConnection(t *testing.T) {\n dsn := "host=localhost port=5432 user=test password=test dbname=test sslmode=disable"\n db, err := sql.Open("postgres", dsn)\n if err != nil {\n t.Fatalf("failed to open db: %v", err)\n }\n defer db.Close()\n \n if err := db.Ping(); err != nil {\n t.Fatalf("failed to ping db: %v", err)\n }\n}\nتشغيل اختبارات التكامل فقط: go test -tags=integration ./...
2.2. الموكات (Mocks) والنماذج الوهمية (Stubs)
\nلعزل الاختبارات، استخدم الواجهات والموكات. مثال مع المكتبة الشهيرة gomock:
// user.go\ntype UserRepository interface {\n GetUser(id int) (*Us