لماذا تستغرق مجموعة اختباراتك إحدى عشرة دقيقة
المجموعة البطيئة مجموعة يتخطاها الناس. والوقت لا يكاد يكون في التأكيدات: بل في إعادة بناء قاعدة البيانات، وتجزئة كلمات المرور، والخروج إلى الشبكة دون قصد.
المجموعة التي تستغرق إحدى عشرة دقيقة لا تُشغَّل قبل الدفع. يشغّلها التكامل المستمر، بعد أن يكون السياق قد ضاع، فيتحول تصحيح من خمس ثوانٍ إلى رحلة ذهاب وإياب من عشرين دقيقة. وفي النهاية يدفع أحدهم فوق بناء أحمر لأنه واثق إلى حد ما أنه لا علاقة له.
فسرعة المجموعة إذن ليست راحة للمطوّر. بل هي ما يقرر هل تؤدي الاختبارات عملها.
قِس قبل أن تخمّن
php artisan test --profileيطبع هذا أبطأ الاختبارات. وتكاد تستحوذ حفنة حالات دائمًا على أغلب زمن الساعة، ويكون الإصلاح محددًا لا معماريًا.
قاعدة البيانات، وهي أغلبه عادةً
الهجرة عند كل اختبار. الـ trait المسمى RefreshDatabase يلف كل اختبار في
معاملة ويعكسها، وهذا سريع. أما DatabaseMigrations فيشغّل كل هجرة لكل اختبار،
وهذا ليس كذلك. وفي مشروع فيه مئتا هجرة، ذلك الفارق هو المشكلة كلها.
هجرات بدل تفريغ مخطط. حتى مرة واحدة لكل جولة، إعادة تشغيل مئتي هجرة تستغرق أطول من تحميل نتيجتها:
php artisan schema:dump --pruneفتحمّل المجموعة عندئذٍ ملف SQL واحدًا. وهذا أكبر مكسب منفرد في شيفرة ناضجة ويكلّف أمرًا واحدًا.
مصانع تنشئ أكثر مما يحتاجه الاختبار. مصنع بسلسلة has() ينشئ عشرين سجلًا
مرتبطًا لاختبار يقرأ حقلًا واحدًا. أنشئ الحد الأدنى، واستخدم make() بدل
create() حيث لا شيء يلمس قاعدة البيانات.
تجزئة كلمات المرور، وهي ما لا يتوقعه أحد
التجزئة بطيئة عمدًا - ذلك غرضها. والمجموعة التي تنشئ مئات المستخدمين عبر مصنع تدفع تلك الكلفة مئات المرات.
// tests/TestCase.php أو Pest.php
Hash::driver('bcrypt')->setRounds(4);في المجموعات كثيرة التجهيزات المستخدمة، خفّض هذا وحده أزمنة التشغيل إلى النصف.
اختبارات تخرج إلى الشبكة
الفئة الأشد ضررًا، لأنها بطيئة وغير موثوقة في آن. الاختبار الذي يستدعي واجهة حقيقية ينتظر خادم شخص آخر ويفشل حين يكون لذلك الخادم يوم سيئ.
Http::preventStrayRequests();ضع ذلك في حالة الاختبار الأساسية. فأي اختبار يجري استدعاء HTTP غير مُحاكى يفشل فورًا ويخبرك أيّه - وستجد بعضها لم تكن تعلم بوجودها.
وينطبق الأمر نفسه على الطوابير والبريد والتخزين. محاكاةً تكون فورية؛ وحقيقيةً تؤدي عملًا لا يؤكّد عليه الاختبار.
الانتظار والاستطلاع
الأمر sleep(2) في اختبار ينتظر شيئًا غير متزامن ثانيتان في كل جولة، إلى الأبد،
وهو إما طويل أكثر من اللازم وإما - على جهاز أبطأ - غير كافٍ.
مساعدات الوقت في Laravel تجعل الانتظار غير ضروري: جمّد الوقت، وسافر إلى الأمام، وأكّد. وللعمل غير المتزامن فعلًا، شغّل الطابور تزامنيًا في الاختبارات بدل انتظار عامل.
اختبارات المتصفح، وهي في سلة أخرى
هي بطيئة لأنها تقود متصفحًا حقيقيًا، ولا حيلة تجعل ذلك سريعًا. والجواب الكمّ لا السرعة: عدد صغير يغطي المسارات المهمة، يُشغَّل منفصلًا عن المجموعة السريعة، كي لا يقف بين مطوّر وتغذيته الراجعة.
التوازي، متى استحقته الاختبارات
php artisan test --parallelتحسّن شبه خطي على جهاز متعدد النوى - وسيكشف كل اختبار كان يعتمد بهدوء على آخر. ملفات تجهيزات مشتركة، ومعرّفات مرمَّزة، وسجل يُفترض وجوده لأن اختبارًا سابقًا أنشأه.
تلك الإخفاقات تستحق أن تحدث. الاختبار الذي لا ينجح إلا إذا شُغِّل بعد آخر ليس اختبارًا بل تسلسلًا، وكان سيفشل عشوائيًا في التكامل المستمر على أي حال.
الهدف
تحت الدقيقة للمجموعة التي يشغّلها الناس قبل الدفع، وكل ما هو أبطأ - اختبارات المتصفح، والتكامل مقابل خدمات حقيقية - في مهمة منفصلة تعمل بعدها.
والمقياس ليس التغطية ولا الأناقة. بل هل تشغيل الاختبارات شيء يفعله مطوّر دون أن يقرره.
المجموعة التي لا يشغّلها أحد هي أيضًا مجموعة لا يستطيع أحد أن يرقّي خلفها. ولهذا فبناؤها هو المرحلة الأولى من الترقية لا الأخيرة. وإن كانت المجموعة السريعة تعمل على SQLite بينما يعمل الإنتاج على MySQL، فلتلك المقايضة كلفة، وليست صغيرة دائمًا.
