الوحدة 5 · استراتيجية الاختبار
الاختبار في CI واختيار ما لا تختبره
18 دقراءة 3 داختر بحكمة
الدرس في لمحة
تشغّل مجموعة اختبارات متعدّدة الطبقات في CI باستخدام GitHub Actions كي يحصل كل pull request على تغذية راجعة سريعة وموثوقة، وتقرّر أيّ الاختبارات تتخطّاها أو تحذفها لأن تكلفتها أكبر مما تلتقطه.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
الاختبارات على حاسوبك تحميك أنت. أما الاختبارات في CI (التكامل المستمر) فتحمي الفريق كله، مع كل pull request، سواء تذكّر أحدهم تشغيلها أم لا. يجمع هذا الدرس الأخير القطع معًا: pipeline يشغّل الطبقات التي بنيتها بالترتيب الصحيح، وحسن التقدير في ما يستحق اختبارًا من الأساس.
pipeline من الأسرع إلى الأبطأ
الترتيب مهم بقدر المحتوى. الفحوص الرخيصة التي تفشل كثيرًا تأتي أولًا، فيكلّفك الخطأ المطبعي 30 ثانية من الانتظار بدل 12 دقيقة:
وإليك ذلك على شكل workflow في GitHub Actions:
name: Test
on:
pull_request:
push:
branches: [main]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npm run lint
- run: npx tsc --noEmit
- run: npx vitest run --coverage
e2e:
needs: unit
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shard }}/3
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report-${{ matrix.shard }}
path: playwright-report/
retention-days: 14بضعة تفاصيل تؤدّي عملًا حقيقيًا:
needs: unitيعني أن مهمّة الـ end-to-end لا تبدأ إلا حين تنجح اختبارات الوحدات. لا فائدة من تشغيل المتصفّحات لكود يفشل فيtsc.--shard=1/3يقسّم مجموعة Playwright على ثلاثة أجهزة تعمل بالتوازي. (وللحصول على تقرير واحد مدمج، انتقل إلى المُبلّغblobوشغّلnpx playwright merge-reportsفي مهمّة أخيرة.)if: ${{ !cancelled() }}يرفع التقرير حتى حين تفشل الاختبارات، وهذا بالضبط حين تحتاج إلى الـ traces من درس تتبّع الأخطاء.CIيُضبط تلقائيًا على خوادم GitHub، فتعمل Vitest مرة واحدة بدل المراقبة، ويفشل أي.onlyشارد، ويبدأ عملretries: process.env.CI ? 2 : 0في إعدادات Playwright.
وأخيرًا، في إعدادات المستودع، اجعل هذه المهامّ فحوص حالة إلزامية (required status checks) على main، كي يمنع الـ pipeline الأحمر الـ merge فعلًا. فالـ pipeline الذي لا يُلزَم أحد بالنجاح فيه مجرّد اقتراح.
اختيار ما لا تختبره
كل اختبار يكلّف وقت كتابة، ودقائق CI، وصيانة. وبعضها يكلّف أكثر مما سيلتقطه في أي وقت:
- الكود التافه: دالة getter تعيد حقلًا، أو ثابت (
expect(CURRENCY).toBe('USD')يعيد قول ما في الكود). - سلوك أطر العمل والمكتبات: أن
useStateيحدّث الـ state، أو أنArray.prototype.sortيرتّب، أو أن مكتبة التواريخ لديك تنسّق التواريخ. هذا يختبره القائمون على صيانتها. - التفاصيل الداخلية: أيّ دالة مساعدة خاصة استُدعيت، وكم مرة أُعيد render مكوّن ما، والحالة الداخلية. هذه تفشل مع إعادة الهيكلة وتنجح مع الأخطاء.
- التنسيق: أسماء الأصناف وقيم البكسل في الـ unit tests. وإن كانت الانتكاسات البصرية مهمّة، فاستخدم اختبارات لقطات الشاشة في متصفّح حقيقي لبضع شاشات أساسية.
- الكود المؤقّت: التجارب والنماذج الأولية التي توشك على حذفها.
وكن مستعدًا للحذف. الاختبار الذي لم يفشل قط لسبب حقيقي خلال عامين، أو الذي ينكسر مع كل إعادة هيكلة، أو المتخطّى بشكل دائم، عبء. وحذفه مساهمة.
الصورة الكاملة
لديك الآن كل الطبقات: الفحوص الساكنة، والـ unit tests التي تثبّت السلوك والحدود، واختبارات الواجهة التي تتصرّف كالمستخدمين، واختبارات الـ end-to-end التي تنتظر بشكل صحيح وتبقى معزولة، والأدوات للحكم عليها: الـ coverage للفجوات، والـ mutation testing للقوة. والسؤال الذي يقف خلف كل ذلك هو نفسه سؤال الدرس الأول: هل سيفشل هذا الاختبار بسبب خطأ قد يصل فعلًا إلى المستخدمين، بسرعة، وبرسالة تقول ما الذي انكسر؟ احتفظ بالاختبارات التي تجيب بنعم، وأصلح التي يمكن أن تجيب بها، واحذف البقية.
يطلب منك التمرين اتخاذ هذه القرارات على قائمة اختبارات تبدو حقيقية.
أهم النقاط
- رتّب CI من الأسرع إلى الأبطأ (الـ lint والأنواع، ثم اختبارات unit وintegration، ثم end-to-end) كي توقف حالات الفشل الرخيصة التشغيل مبكرًا.
- اجعل مهامّ الاختبار فحوصًا إلزامية (required checks) على الفرع الرئيسي، وارفع تقارير Playwright كـ artifacts، ووزّع مجموعات الـ end-to-end البطيئة على عدة أجهزة بالـ sharding.
- أبقِ الـ pipeline كله سريعًا بما يكفي كي ينتظره الناس؛ فالـ pipeline الذي يستغرق 40 دقيقة يُتجاوَز.
- لا تختبر الكود التافه، ولا سلوك أطر العمل أو المكتبات، ولا التنسيق، ولا التفاصيل الداخلية؛ واحذف الاختبارات التي لا تفشل أبدًا لسبب حقيقي.
- يستحق الاختبار مكانه حين يفشل بسبب خطأ قد يصل فعلًا إلى المستخدمين، بسرعة، وبرسالة واضحة.