اختبار التكامل في Go مع Docker: المستوى المتقدم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

اختبار التكامل في Go مع Docker: من النظرية إلى التطبيق المتقدم

\n\n

اختبار التكامل هو مرحلة حاسمة في تطوير الخدمات المصغرة بلغة Go. بدون التحقق من التفاعل مع قواعد البيانات، قوائم انتظار الرسائل وواجهات API الخارجية، فإنك تخاطر بالحصول على "يعمل على جهازي" في أسوأ صوره. يتيح Docker إنشاء بيئات نظيفة ومعزولة لكل اختبار، بينما تتيح التقنيات المتقدمة القيام بذلك بسرعة وبشكل متوازي وبدون ألم. في هذه المقالة، سنستعرض الأساليب الواقعية التي يستخدمها كبار المهندسين.

\n\n

1. لماذا Docker هو الشريك المثالي لاختبارات التكامل في Go

\n\n

يتم تجميع Go في ملف ثنائي ثابت، مما يجعله مرشحًا مثاليًا للحاويات. في اختبار التكامل، يحل Docker ثلاث مشاكل رئيسية:

\n
    \n
  • العزل: يحصل كل اختبار على قاعدة بيانات جديدة أو Redis خالية من "القمامة" من عمليات التشغيل السابقة.
  • \n
  • قابلية إعادة الإنتاج: تعمل الاختبارات بنفس الطريقة على جهاز المطور، في CI/CD وفي الإنتاج.
  • \n
  • السرعة: يتم تشغيل الحاويات المضبوطة بشكل صحيح في ثوانٍ وليس دقائق.
  • \n
\n\n

الخطأ الرئيسي للمبتدئين هو تشغيل حاوية واحدة لجميع الاختبارات. يؤدي هذا إلى حالات سباق وأخطاء "عائمة". النهج المتقدم: حاوية واحدة لكل حزمة اختبار أو حتى لكل اختبار فردي.

\n\n

2. Testcontainers لـ Go: حاويات مُدارة من الكود

\n\n

تتيح مكتبة testcontainers-go إدارة حاويات Docker مباشرة من اختبارات Go. هذا أكثر أناقة من كتابة نصوص bash أو الاعتماد على Docker Compose المُشغل مسبقًا.

\n\n

التثبيت:

\n
go get github.com/testcontainers/testcontainers-go\ngo get github.com/testcontainers/testcontainers-go/modules/postgres
\n\n

مثال: اختبار PostgreSQL مع الترحيلات:

\n
package integration_test\n\nimport (\n    "context"\n    "database/sql"\n    "testing"\n    "time"\n\n    "github.com/testcontainers/testcontainers-go"\n    "github.com/testcontainers/testcontainers-go/modules/postgres"\n    "github.com/testcontainers/testcontainers-go/wait"\n    _ "github.com/lib/pq"\n)\n\nfunc TestUserRepository(t *testing.T) {\n    ctx := context.Background()\n\n    pgContainer, err := postgres.RunContainer(ctx,\n        testcontainers.WithImage("postgres:16-alpine"),\n        postgres.WithDatabase("testdb"),\n        postgres.WithUsername("user"),\n        postgres.WithPassword("password"),\n        testcontainers.WithWaitStrategy(\n            wait.ForLog("database system is ready to accept connections").\n                WithOccurrence(2).\n                WithStartupTimeout(30*time.Second),\n        ),\n    )\n    if err != nil {\n        t.Fatal(err)\n    }\n    defer pgContainer.Terminate(ctx)\n\n    connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable")\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    db, err := sql.Open("postgres", connStr)\n    if err != nil {\n        t.Fatal(err)\n    }\n    defer db.Close()\n\n    // هنا نقوم بتنفيذ الترحيلات والاختبارات\n    _, err = db.Exec(`CREATE TABLE IF NOT EXISTS users (\n        id SERIAL PRIMARY KEY,\n        name TEXT NOT NULL\n    )`)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // نختبر المستودع\n    repo := NewUserRepository(db)\n    user, err := repo.Create(ctx, "Alice")\n    if err != nil {\n        t.Fatal(err)\n    }\n    if user.Name != "Alice" {\n        t.Errorf("expected Alice, got %s", user.Name)\n    }\n}
\n\n

النقاط الرئيسية:

\n
    \n
  • WithWaitStrategy — ننتظر حتى يصبح PostgreSQL جاهزًا حقًا، وليس فقط تشغيل الحاوية.
  • \n
  • defer pgContainer.Terminate(ctx) — يضمن التنظيف حتى في حالة الذعر.
  • \n
  • يحصل كل اختبار على مثيل قاعدة البيانات الخاص به — عزل كامل.
  • \n
\n\n

3. الاختبارات المتوازية وإدارة الموارد

\n\n

يدعم Go التوازي

المدونات

توصيات الكتب