اختبار التكامل في Go مع Docker: من النظرية إلى التطبيق المتقدم
\n\nاختبار التكامل هو مرحلة حاسمة في تطوير الخدمات المصغرة بلغة Go. بدون التحقق من التفاعل مع قواعد البيانات، قوائم انتظار الرسائل وواجهات API الخارجية، فإنك تخاطر بالحصول على "يعمل على جهازي" في أسوأ صوره. يتيح Docker إنشاء بيئات نظيفة ومعزولة لكل اختبار، بينما تتيح التقنيات المتقدمة القيام بذلك بسرعة وبشكل متوازي وبدون ألم. في هذه المقالة، سنستعرض الأساليب الواقعية التي يستخدمها كبار المهندسين.
\n\n1. لماذا Docker هو الشريك المثالي لاختبارات التكامل في Go
\n\nيتم تجميع Go في ملف ثنائي ثابت، مما يجعله مرشحًا مثاليًا للحاويات. في اختبار التكامل، يحل Docker ثلاث مشاكل رئيسية:
\n- \n
- العزل: يحصل كل اختبار على قاعدة بيانات جديدة أو Redis خالية من "القمامة" من عمليات التشغيل السابقة. \n
- قابلية إعادة الإنتاج: تعمل الاختبارات بنفس الطريقة على جهاز المطور، في CI/CD وفي الإنتاج. \n
- السرعة: يتم تشغيل الحاويات المضبوطة بشكل صحيح في ثوانٍ وليس دقائق. \n
الخطأ الرئيسي للمبتدئين هو تشغيل حاوية واحدة لجميع الاختبارات. يؤدي هذا إلى حالات سباق وأخطاء "عائمة". النهج المتقدم: حاوية واحدة لكل حزمة اختبار أو حتى لكل اختبار فردي.
\n\n2. Testcontainers لـ Go: حاويات مُدارة من الكود
\n\nتتيح مكتبة testcontainers-go إدارة حاويات Docker مباشرة من اختبارات Go. هذا أكثر أناقة من كتابة نصوص bash أو الاعتماد على Docker Compose المُشغل مسبقًا.
التثبيت:
\ngo get github.com/testcontainers/testcontainers-go\ngo get github.com/testcontainers/testcontainers-go/modules/postgres\n\nمثال: اختبار PostgreSQL مع الترحيلات:
\npackage 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
3. الاختبارات المتوازية وإدارة الموارد
\n\nيدعم Go التوازي