كل المقالات والفيديوهات

ثبّت سلوك الكود قبل أن تمسّه

قبل أي ترحيل، دع الوكيل يكتب الاختبارات التي لم يحظَ بها الكود القديم قط. ثم رحّل وأعد تشغيلها. كل اختبار أحمر قرار، لا مفاجأة.

Adil Ben Moussa
07 نوفمبر 2026· 4 دقائق
ثبّت سلوك الكود قبل أن تمسّه

أعاد الجزء السابق إلى الوكيل خطته التي تبدأ بكلمة «احذف»، ووضع مكانها ثلاثة ملفات مهام. يتناول هذا الجزء أولها، المهمة 000، وهي تأتي قبل أي كود جديد. فحتى الخطة الجيدة تُغري بالبدء بالميزة الجديدة. أما البداية الصحيحة فهي الكود القديم () كما هو اليوم.

صورة لا حكم

قبل أي خطوة في الترحيل، اكتب الاختبارات التي لم يحظَ بها الكود القديم قط. لا للكود الجديد، بل للقديم، كما هو الآن.

ابدأ من حيث السهولة: الأجزاء النقية. دالة للتحقق من صحة المدخلات ()، أو دالة تنسيق، أو خدمة تحوّل استجابةً إلى نموذج عرض (). في المستودع التجريبي، يضم مكوّن التحويلات القديم دالة validate() فيها سبع قواعد في اثني عشر سطراً تقريباً، ويمكنك تثبيتها في أقل من نصف ساعة. والوكيل بارع في هذا، بل هو أبرع فيه من أي عمل آخر في الترحيل، بشرط واحد يُكتب في ملف المهمة: لا يحق له تغيير الكود الذي يختبره. يقرأ validate()، ثم يكتب ملف اختبار فيه حالة لكل قاعدة، بما فيها القاعدة الخاطئة، ثم يتوقف.

تسجّل هذه الاختبارات ما يفعله الكود القديم، بأخطائه. وهذا هو المقصود تماماً.

إنها لا تحكم على الكود، بل تلتقط له صورة.

ثم رحّل، ثم قارن

نفّذ الآن خطة الجزء السابق: ابنِ الميزة الجديدة بجانب القديمة، وبدّل المسار. ثم شغّل الحالات نفسها على الكود الجديد.

كل اختبار أخضر سلوكٌ حُفظ دون أن يضطر أحد إلى تذكّره. وكل اختبار أحمر قرار يُتخذ عن وعي: إما خطأ تختار الآن إصلاحه، في مستقل، مع تعديل الاختبار ليعكس ذلك، وإما انتكاسة () اكتشفتها قبل أن يكتشفها أحد العملاء.

تبدو كتابة اختبارات لكود توشك على حذفه هدراً للوقت. لكنها ليست كذلك: فهي السجل الوحيد لما كان الكود القديم يفعله فعلاً، وعلى أساسها يُقاس الترحيل. ومع الوكيل تصبح كتابة هذا السجل رخيصة، والقاعدة لم تتغير: ثبّت أولاً، ثم رحّل، ثم قارن.

ملف المهمة للخطوة صفر

نسخة مختصرة من docs/tasks/000-pin-legacy-transfers.md في المستودع التجريبي:

# المهمة 000: تثبيت سلوك libs/legacy/transfers قبل ترحيل أي شيء
 
## النطاق
- مسموح بتعديل: لا شيء مما هو موجود
- ملفات جديدة مسموحة في: libs/legacy/transfers/src/lib/*.spec.ts
 
## خارج النطاق
- كل ملف ليس ملف اختبار. إن بدت validate() خاطئة، فاكتب الاختبار الذي يثبت ما تفعله اليوم، وأضف `// TODO 000: looks wrong because …` فوق الاختبار، لا في الكود
 
## ينتهي حين
- [ ] حالة it() واحدة لكل فرع من فروع TransferComponent.validate()، تحمل اسم السلوك الذي تسجّله
- [ ] حالة it() واحدة لكل دالة عامة في TransferService، عبر AccountsStore الحقيقي مع FakeBackend فوري
- [ ] كل توقّع يتحقق من نص أو رقم مكتوب كما هو، لا من قيمة يعيد الكود المختبَر حسابها
- [ ] ينجح npm run test
- [ ] الأمر git diff --stat main -- ':!*.spec.ts' لا يطبع شيئاً (لم يتغير شيء خارج ملفات الاختبار)
- [ ] الـ diff ≤ 300 سطر

بند القيمة المكتوبة كما هي أهم مما يبدو. فالوكيل سيكتب عن طيب خاطر expect(error).toBe(blockerMessage['insufficient-funds']): هنا تُقرأ القيمة المتوقعة من الكود بدل أن تُكتب صراحة، فيوافق الاختبار الكود مهما قال. أما النص الثابت 'This account is closed.' فصورة، والقيمة المعاد حسابها مرآة.

برومبت · ثبّت الكود القديم

اقرأ AGENTS.md و docs/tasks/000-pin-legacy-transfers.md. اكتب اختبارات توصيف (characterisation tests) لدالة validate() في libs/legacy/transfers/src/lib/transfer.component.ts ولـ TransferService.

القواعد: لا تعدّل أي ملف ليس ملف *.spec.ts جديداً. اكتب it() واحدة لكل قاعدة أو فرع، تحمل اسم السلوك الذي تسجّله. تحقّق من نصوص وأرقام مكتوبة كما هي. وحيث يبدو السلوك الحالي خطأً، أبقِ الاختبار مطابقاً لهذا السلوك، وأضف فوقه // TODO 000: looks wrong because …. شغّل npm run test وأخبرني بالعدد.

اقرأ ملاحظات التي يتركها. فهي قائمة الأخطاء في وحدتك القديمة، كتبها شيء قرأ كل فرع فيها.

ثبّت الشاشة أيضاً

اختبارات الوحدة () تثبّت القواعد، لكنها لا تثبّت الشاشة، والشاشة هي الجزء الذي توشك على إعادة كتابته. لذلك أضف لكل تدفّق حرج اختباراً واحداً يفتح عنوان ، لا مكوّناً. وتتيح RouterTestingHarness في ذلك ضمن تشغيل الاختبارات نفسه، دون الحاجة إلى متصفح:

const harness = await RouterTestingHarness.create('/transfers/new/a-current');

املأ الحقول عبر تسمياتها ()، واقرأ الرسالة الظاهرة على الشاشة، ولا تعتمد أبداً على صنف أو اسم مكوّن. فالاختبار لا يعرف سوى المسار ()، ولذلك يعرض ما يشير إليه المسار أيّاً كان: المكوّن القديم اليوم، والجديد بعد تبديل المسار. وفي المستودع التجريبي، يعمل ملف الاختبار نفسه قبل تبديل المسار وبعده. وهذا هو شرط «ينتهي حين» للشاشة بعد تحديثها. وإن كانت لدى فريقك اختبارات من طرف إلى طرف ()، فهي تؤدي المهمة نفسها.

ما لا يعنيه هذا

هذا لا يعني تغطية كاملة. ثبّت ما هو رخيص وما هو مخيف: القواعد، والمال، والتواريخ، وكل ما قد يلاحظه العميل. أما تفاصيل القالب () فلا تستحق اختبارات وحدة، واختبار العنوان يغطي ما يهم منها. إنها مسألة ظهيرة واحدة، لا دورة عمل () كاملة.

مع بداية الأسبوع

  • اختر وحدة قديمة واحدة. اطلب من الوكيل أن يعدّد دوالها النقية، وثبّتها أولاً.
  • قبل أن تمسّ أي قالب، اكتب اختباراً واحداً يفتح عنوانه ويمرّ بالتدفّق الرئيسي.
  • اكتب «لم يتغير شيء خارج *.spec.ts» ضمن شروط «ينتهي حين»، وتحقّق منه بـ git diff --stat.
  • أبقِ الاختبارات نفسها تعمل على الكود الجديد إلى أن يُحذف الكود القديم.

التمرين: الوحدة 2، المشهد 0 في المستودع التجريبي. البداية: git switch -c m2/you main. الحل المرجعي: ملفا الاختبار القديمان في libs/legacy/transfers/src/lib/ على origin/track1/m2-ngmodule-solution~1، أي الحالة التي تسبق الحذف مباشرة.

كل هذا يعتمد على المترجم ()، وعلى أداة تشغيل اختبارات ترى كل قالب. أما الجزء التالي فيتناول كوداً قديماً لا يصحّ فيه ذلك: داخل تطبيق Angular.


المسار 1، الجزء 5 من السلسلة المجانية المرافقة لورشة سريع وآمن: الذكاء الاصطناعي في قاعدة كود Angular الخاصة بك.

شارك هذا المقال
ورشة

سريع وآمن: الذكاء الاصطناعي في كود Angular الخاص بكم

دع الوكلاء يكتبون Angular تحت قواعد، وحدّث الكود القديم دون أن تكسر الإنتاج، وحافظ على أمانه، في ثلاثة أيام، على كود بحجم المؤسسات.

اطّلع على البرنامج
اقرأ بعد ذلك
لوحات InDesignالمنتجلوحات InDesignالترحيل من AngularJS إلى Angular · Angular | AngularJS | Adobe InDesignالمشروع