اختبار التكامل لـ TypeScript مع Docker: من الألف إلى الياء
\n\nالتطوير الحديث باستخدام TypeScript لا يتطلب فقط اختبارات الوحدات عالية الجودة، بل يتطلب أيضًا التحقق من تفاعل المكونات في ظروف حقيقية. يتيح اختبار التكامل باستخدام Docker إنشاء بيئات معزولة مع قواعد البيانات وذاكرة التخزين المؤقت والخدمات الأخرى، مما يجعل الاختبارات أقرب ما يمكن إلى بيئة الإنتاج. في هذه المقالة، سنشرح كيفية إعداد اختبار التكامل لتطبيق TypeScript باستخدام Docker، وسنكتب اختبارات لـ REST API، وسنستعرض أفضل الممارسات.
\n\nلماذا Docker لاختبار التكامل؟
\n\nيحل Docker إحدى المشكلات الرئيسية لاختبارات التكامل - الاعتماد على الخدمات الخارجية. بدلاً من استخدام قاعدة بيانات مشتركة أو Redis، والتي قد تكون مشغولة من قبل مطورين آخرين، نقوم بإنشاء حاويات معزولة لكل تشغيل اختبار. وهذا يضمن:
\n\n- \n
- نقاء الاختبارات: كل تشغيل يبدأ من حالة فارغة \n
- قابلية التكرار: تعمل الاختبارات بنفس الطريقة على أي جهاز \n
- العزل: لا تؤثر الاختبارات على بعضها البعض ولا على بيانات الإنتاج \n
- السرعة: يتم رفع الحاويات في ثوانٍ \n
إعداد المشروع و Docker Compose
\n\nلنقم بإنشاء مشروع بسيط باستخدام TypeScript مع Express و PostgreSQL. مهمتنا هي اختبار نقطة نهاية إنشاء المستخدم. لنبدأ بملف docker-compose.test.yml:
version: '3.8'\nservices:\n db_test:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: testdb\n POSTGRES_USER: testuser\n POSTGRES_PASSWORD: testpass\n ports:\n - "5433:5432"\n healthcheck:\n test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]\n interval: 5s\n timeout: 5s\n retries: 5\n\n app_test:\n build:\n context: .\n dockerfile: Dockerfile.test\n environment:\n DATABASE_URL: postgres://testuser:testpass@db_test:5432/testdb\n NODE_ENV: test\n ports:\n - "3001:3000"\n depends_on:\n db_test:\n condition: service_healthy\n\nالآن لنقم بإنشاء Dockerfile.test لبناء تطبيق TypeScript:
FROM node:18-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nRUN npm run build\nCMD ["node", "dist/index.js"]\n\nكتابة اختبارات التكامل باستخدام Jest و Supertest
\n\nلنقم بتثبيت الحزم اللازمة:
\n\nnpm install --save-dev jest @types/jest ts-jest supertest @types/supertest\n\nلنقم بإنشاء تكوين Jest (jest.config.ts):
export default {\n preset: 'ts-jest',\n testEnvironment: 'node',\n testMatch: ['**/__tests__/**/*.test.ts'],\n setupFilesAfterSetup: ['./jest.setup.ts'],\n testTimeout: 30000,\n};\n\nسيكون ملف jest.setup.ts مسؤولاً عن تشغيل وإيقاف حاويات Docker:
import { execSync, spawn } from 'child_process';\nimport path from 'path';\n\nconst composeFile = path.resolve(__dirname, '../docker-compose.test.yml');\n\nbeforeAll(async () => {\n // رفع الحاويات\n execSync(`docker-compose -f ${composeFile} up -d --build`, { stdio: 'inherit' });\n \n // الانتظار حتى يصبح التطبيق متاحًا\n await new Promise((resolve) => setTimeout(resolve, 5000));\n});\n\nafterAll(async () => {\n // إيقاف وإزالة الحاويات\n execSync(`docker-compose -f ${composeFile} down -v`, { stdio: 'inherit' });\n});\n\nالآن لنكتب الاختبار نفسه. لنقم بإنشاء ملف __tests__/users.test.ts:
import request from 'supertest';\n\nconst BASE_URL = 'http://localhost:3001/api';\n\ndescribe('اختبارات تكامل واجهة برمجة تطبيقات المستخدمين', () => {\n const testUser = {\n name: 'إيفان إيفانوف',\n email: 'ivan@example.com',\n password: 'securePass123'\n };\n\n