الاختبارات و TDD

خطوط أنابيب الاختبار في التكامل والنشر المستمر

20 دقيقة الدرس 26 من 35

خطوط أنابيب الاختبار في التكامل والنشر المستمر

تعمل خطوط أنابيب التكامل المستمر والنشر المستمر (CI/CD) على أتمتة عملية الاختبار الخاصة بك، مما يضمن اختبار كل تغيير في الكود بشكل شامل قبل الوصول إلى الإنتاج. يغطي هذا الدرس إعداد خطوط أنابيب الاختبار الآلي باستخدام منصات CI/CD الشهيرة.

فهم اختبار CI/CD

يقوم اختبار CI/CD تلقائيًا بتشغيل مجموعة الاختبارات الخاصة بك كلما تم دفع تغييرات الكود إلى المستودع الخاص بك. هذا يوفر ردود فعل فورية ويمنع الأخطاء من الوصول إلى الإنتاج.

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

GitHub Actions لاختبار Laravel

GitHub Actions هي منصة CI/CD قوية متكاملة مباشرة في مستودعات GitHub. إليك سير عمل اختبار Laravel شامل:

# .github/workflows/tests.yml name: Laravel Tests on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] jobs: laravel-tests: runs-on: ubuntu-latest strategy: matrix: php-version: [8.1, 8.2, 8.3] laravel-version: [10.*, 11.*] services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: testing ports: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 redis: image: redis:7 ports: - 6379:6379 options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} extensions: mbstring, dom, fileinfo, mysql, redis coverage: xdebug - name: Cache Composer dependencies uses: actions/cache@v3 with: path: vendor key: composer-${{ matrix.php-version }}-${{ matrix.laravel-version }}-${{ hashFiles('**/composer.lock') }} restore-keys: | composer-${{ matrix.php-version }}-${{ matrix.laravel-version }}- - name: Install Dependencies run: | composer require "laravel/framework:${{ matrix.laravel-version }}" --no-interaction --no-update composer install --prefer-dist --no-interaction --no-progress - name: Copy Environment File run: cp .env.example .env - name: Generate Application Key run: php artisan key:generate - name: Directory Permissions run: chmod -R 777 storage bootstrap/cache - name: Run Database Migrations env: DB_CONNECTION: mysql DB_HOST: 127.0.0.1 DB_PORT: 3306 DB_DATABASE: testing DB_USERNAME: root DB_PASSWORD: password run: php artisan migrate --force - name: Execute Unit Tests env: DB_CONNECTION: mysql DB_HOST: 127.0.0.1 DB_PORT: 3306 DB_DATABASE: testing DB_USERNAME: root DB_PASSWORD: password REDIS_HOST: 127.0.0.1 REDIS_PORT: 6379 run: php artisan test --parallel --coverage --min=80 - name: Upload Coverage Reports uses: codecov/codecov-action@v3 with: files: ./coverage.xml flags: unittests name: codecov-umbrella

إعداد GitLab CI

يوفر GitLab CI/CD ميزات قوية لاختبار تطبيقات Laravel مع سجل الحاويات المدمج وخيارات النشر:

# .gitlab-ci.yml image: php:8.2-fpm variables: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: testing MYSQL_USER: laravel MYSQL_PASSWORD: laravel DB_HOST: mysql stages: - build - test - deploy cache: key: ${CI_COMMIT_REF_SLUG} paths: - vendor/ - node_modules/ before_script: - apt-get update -yqq - apt-get install -yqq git libzip-dev libpq-dev libcurl4-gnutls-dev - docker-php-ext-install pdo_mysql zip - curl -sS https://getcomposer.org/installer | php - php composer.phar install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts - cp .env.example .env - php artisan key:generate - php artisan config:clear build: stage: build script: - composer validate - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts - npm install - npm run build artifacts: paths: - vendor/ - node_modules/ - public/build/ expire_in: 1 hour unit-tests: stage: test services: - mysql:8.0 dependencies: - build script: - php artisan migrate --seed - php artisan test --testsuite=Unit --coverage-cobertura=coverage.xml artifacts: reports: coverage_report: coverage_format: cobertura path: coverage.xml coverage: '/^\s*Lines:\s*\d+.\d+\%/' feature-tests: stage: test services: - mysql:8.0 - redis:latest dependencies: - build script: - php artisan migrate --seed - php artisan test --testsuite=Feature retry: max: 2 when: runner_system_failure browser-tests: stage: test services: - mysql:8.0 - selenium/standalone-chrome:latest dependencies: - build script: - php artisan dusk:chrome-driver --detect - php artisan migrate --seed - php artisan serve > /dev/null 2>&1 & - sleep 5 - php artisan dusk artifacts: when: on_failure paths: - tests/Browser/screenshots/ - tests/Browser/console/ expire_in: 7 days

التنفيذ المتوازي للاختبارات

تشغيل الاختبارات بشكل متوازٍ يقلل بشكل كبير من وقت تنفيذ خط الأنابيب:

# GitHub Actions مع الاختبار المتوازي jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: test-suite: [Unit, Feature, Browser] steps: - uses: actions/checkout@v4 - name: Run ${{ matrix.test-suite }} Tests run: php artisan test --testsuite=${{ matrix.test-suite }} --parallel
# إعداد PHPUnit للتنفيذ المتوازي <!-- phpunit.xml --> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false"> <testsuites> <testsuite name="Unit"> <directory suffix="Test.php">./tests/Unit</directory> </testsuite> <testsuite name="Feature"> <directory suffix="Test.php">./tests/Feature</directory> </testsuite> </testsuites> <php> <env name="PARALLEL_TESTING" value="true"/> <env name="PARATEST_PROCESSES" value="4"/> </php> </phpunit>

تقارير الاختبار والقطع الأثرية

تساعد تقارير الاختبار الشاملة في تتبع نتائج الاختبار بمرور الوقت:

// إنشاء تقرير اختبار HTML php artisan test --coverage-html=coverage-report // إنشاء شارة التغطية php artisan test --coverage-clover=coverage.xml // تقرير JUnit XML لأنظمة CI php artisan test --log-junit=junit.xml
أفضل ممارسات تقارير الاختبار:
  • تخزين تقارير الاختبار كقطع أثرية لمدة 30-90 يومًا
  • إنشاء شارات التغطية لملفات README
  • تتبع اتجاهات وقت تنفيذ الاختبار
  • إعداد الإخطارات لفشل الاختبارات
  • أرشفة لقطات الشاشة والسجلات للاختبارات الفاشلة

الاختبار الخاص بالبيئة

تكوين بيئات اختبار مختلفة لسيناريوهات متنوعة:

# .github/workflows/tests.yml jobs: test: runs-on: ubuntu-latest strategy: matrix: environment: [staging, production] steps: - name: Run tests against ${{ matrix.environment }} env: APP_ENV: ${{ matrix.environment }} APP_URL: ${{ secrets[format('{0}_APP_URL', matrix.environment)] }} DB_DATABASE: ${{ secrets[format('{0}_DB_DATABASE', matrix.environment)] }} run: php artisan test

تعبئة قاعدة البيانات في CI

تعبئة قواعد بيانات الاختبار بكفاءة في خط الأنابيب الخاص بك:

// استخدام seeder مخصص لـ CI class CISeed extends Seeder { public function run() { // إنشاء بيانات الاختبار الأساسية فقط User::factory()->count(10)->create(); Product::factory()->count(50)->create(); // تخطي العمليات التي تستغرق وقتًا طويلاً if (!app()->environment('ci')) { $this->call(HeavyDataSeeder::class); } } }
# في سير عمل CI - name: Seed Database run: php artisan db:seed --class=CISeed

استراتيجيات التخزين المؤقت

تحسين سرعة خط الأنابيب باستخدام التخزين المؤقت الذكي:

# التخزين المؤقت في GitHub Actions - name: Cache Composer packages uses: actions/cache@v3 with: path: vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | ${{ runner.os }}-composer- - name: Cache NPM packages uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm- - name: Cache Laravel Routes uses: actions/cache@v3 with: path: bootstrap/cache key: ${{ runner.os }}-routes-${{ hashFiles('routes/*.php') }}

الإخطارات والتكاملات

إعداد الإخطارات لنتائج الاختبار:

# إخطار Slack عند الفشل - name: Notify Slack on Failure if: failure() uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} text: 'Tests failed on ${{ github.ref }}' webhook_url: ${{ secrets.SLACK_WEBHOOK }} - name: Create GitHub Issue on Failure if: failure() uses: actions/github-script@v6 with: script: | github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: 'CI Tests Failed: ${{ github.sha }}', body: 'Tests failed in workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' })

فحص الأمان في خطوط الأنابيب

دمج فحوصات الأمان في خط أنابيب الاختبار الخاص بك:

# فحص ثغرات الأمان - name: Security Audit run: | composer audit npm audit - name: Static Analysis run: | vendor/bin/phpstan analyse --memory-limit=2G vendor/bin/psalm --show-info=true - name: Code Quality Check run: vendor/bin/php-cs-fixer fix --dry-run --diff
الأخطاء الشائعة في خطوط الأنابيب:
  • عدم تخزين التبعيات مؤقتًا (بناء بطيء)
  • تشغيل جميع الاختبارات بشكل متسلسل (وقت ضائع)
  • تجاهل الاختبارات غير المستقرة (نتائج غير موثوقة)
  • متغيرات البيئة المفقودة (فشل الاختبارات)
  • عدم تنظيف الموارد (تسرب الذاكرة)

بوابات النشر

استخدام نتائج الاختبار للتحكم في عمليات النشر:

# النشر فقط إذا نجحت الاختبارات deploy: stage: deploy needs: - unit-tests - feature-tests - browser-tests only: - main script: - echo "Deploying to production..." - ./deploy.sh
تمرين:
  1. أنشئ سير عمل GitHub Actions يشغل اختبارات Laravel الخاصة بك عند الدفع
  2. أضف تنفيذ الاختبار المتوازي لاختبارات Unit و Feature
  3. قم بتكوين خدمات MySQL و Redis
  4. قم بإعداد تقارير تغطية الكود
  5. أضف استراتيجية تخزين مؤقت لتبعيات Composer
  6. إضافي: أضف إخطارات Slack لفشل الاختبارات

مراقبة أداء خط الأنابيب

تتبع وتحسين وقت تنفيذ خط الأنابيب الخاص بك:

// إنشاء تقرير أداء خط الأنابيب php artisan test --profile --testdox // الإخراج: // Time: 00:02.548, Memory: 24.00 MB // // Slowest tests: // - ProductTest::it_can_process_large_orders (1.2s) // - UserTest::it_can_upload_profile_image (0.8s) // - OrderTest::it_can_generate_invoice_pdf (0.6s)
قائمة التحقق من تحسين خط الأنابيب:
  • تخزين التبعيات مؤقتًا بشكل مكثف
  • استخدام التنفيذ المتوازي لمجموعات الاختبار
  • تشغيل الاختبارات السريعة أولاً (استراتيجية الفشل السريع)
  • استخدام معاملات قاعدة البيانات بدلاً من الترحيلات
  • محاكاة استدعاءات API الخارجية
  • استخدام قواعد البيانات في الذاكرة عندما يكون ذلك ممكنًا
  • تنظيف بيانات الاختبار بكفاءة

ملخص أفضل الممارسات

  • تشغيل الاختبارات على كل commit وطلب سحب
  • استخدام matrix builds لاختبار إصدارات PHP/Laravel متعددة
  • تنفيذ الاختبار المتوازي لتقليل وقت التنفيذ
  • تخزين التبعيات والقطع الأثرية مؤقتًا
  • إنشاء وأرشفة تقارير الاختبار
  • إعداد إخطارات ذات مغزى
  • استخدام بوابات النشر لمنع الإصدارات المعطلة
  • مراقبة وتحسين أداء خط الأنابيب
  • تضمين فحوصات الأمان في خط الأنابيب الخاص بك
  • توثيق إعداد CI/CD الخاص بك

يوفر خط أنابيب CI/CD المكون جيدًا الثقة بأن الكود الخاص بك يعمل بشكل صحيح قبل وصوله إلى الإنتاج، مما يتيح عمليات نشر أسرع وأكثر أمانًا.