اختبار التكامل لـ TypeScript مع Docker: المستوى المتقدم
\n\nاختبار التكامل هو مرحلة أساسية للتحقق من تفاعل مكونات النظام. بالنسبة لتطبيقات TypeScript، خاصة في بنية الخدمات المصغرة، يصبح Docker أداة لا غنى عنها. في هذه المقالة، سنستعرض التقنيات المتقدمة لإعداد وتنفيذ اختبارات التكامل باستخدام Docker، والتي ستساعدك على زيادة الموثوقية وسرعة التطوير.
\n\n1. لماذا Docker لاختبار التكامل لـ TypeScript؟
\nيتيح Docker عزل بيئات الاختبار، مما يضمن إمكانية إعادة إنتاجها. بدلاً من الإعداد اليدوي لقواعد البيانات، وطوابير الرسائل، والتبعيات الأخرى، تقوم بتشغيلها في حاويات. هذا مهم بشكل خاص لمشاريع TypeScript، حيث قد تخفي الكتابة الثابتة أخطاء وقت التشغيل التي تظهر فقط أثناء التكامل.
\n- \n
- العزل: يتم تشغيل كل اختبار في بيئة نظيفة \n
- السرعة: يتم تشغيل الحاويات في ثوانٍ \n
- التوازي: يمكن اختبار إصدارات مختلفة من التبعيات في وقت واحد \n
2. إعداد Docker Compose لبيئة الاختبار
\nسنقوم بإنشاء ملف docker-compose.test.yml الذي يشغل PostgreSQL وRedis والتطبيق نفسه للاختبارات:
version: '3.8'\nservices:\n db-test:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: testdb\n POSTGRES_USER: test\n POSTGRES_PASSWORD: testpass\n ports:\n - "5433:5432"\n tmpfs: /var/lib/postgresql/data\n\n redis-test:\n image: redis:7-alpine\n ports:\n - "6380:6379"\n\n app-test:\n build:\n context: .\n dockerfile: Dockerfile.test\n environment:\n DATABASE_URL: postgres://test:testpass@db-test:5432/testdb\n REDIS_URL: redis://redis-test:6379\n depends_on:\n - db-test\n - redis-test\n command: ["npm", "run", "test:integration"]\nهنا نستخدم tmpfs لقاعدة البيانات، بحيث لا يتم حفظ البيانات بين عمليات التشغيل، مما يسرع الاختبارات.
3. الإعداد المتقدم لاختبارات TypeScript باستخدام TestContainers
\nتتيح مكتبة testcontainers إدارة الحاويات مباشرة من كود TypeScript. وهذا يوفر مرونة: يمكنك تشغيل الحاويات فقط لسيناريوهات اختبار معينة.
التثبيت:
\nnpm install --save-dev testcontainers\nمثال على اختبار تكامل مع حاوية ديناميكية:
\nimport { GenericContainer, StartedTestContainer } from 'testcontainers';\nimport { Client } from 'pg';\nimport { UserService } from './user-service';\n\nlet container: StartedTestContainer;\nlet client: Client;\n\nbeforeAll(async () => {\n // تشغيل حاوية PostgreSQL\n container = await new GenericContainer('postgres:15-alpine')\n .withEnvironment({\n POSTGRES_DB: 'test',\n POSTGRES_USER: 'test',\n POSTGRES_PASSWORD: 'test'\n })\n .withExposedPorts(5432)\n .start();\n\n // الاتصال بقاعدة بيانات الاختبار\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n client = new Client({\n host,\n port,\n database: 'test',\n user: 'test',\n password: 'test'\n });\n await client.connect();\n\n // تنفيذ الترحيلات\n await client.query(`\n CREATE TABLE users (\n id SERIAL PRIMARY KEY,\n email VARCHAR(255) UNIQUE NOT NULL,\n name VARCHAR(255)\n )\n `);\n});\n\nafterAll(async () => {\n await client.end();\n await container.stop();\n});\n\ntest('UserService.createUser يقوم بإنشاء مستخدم في قاعدة البيانات', async () => {\n const userService = new UserService(client);\n const user = await userService.createUser({\n email: 'test@example.com',\n name: 'Test User'\n });\n\n const result = await client.query('SELECT * FROM users WHERE email = $1', ['test@example.com']);\n expect(result.rows[0]).toMatchObject({\n email: 'test@example.com',\n