اختبار الطفرة في PHP: قياس نوعي لتغطية الرمز

كيفية تقييم جودة الاختبارات؟ يعتمد الكثيرون على المقياس الأكثر شهرة المعروف للجميع - تغطية الرمز. ولكن هذا هو القياس الكمي ، وليس القياس النوعي. يوضح مقدار تغطية الكود الخاص بك عن طريق الاختبارات ، ولكن ليس مدى جودة كتابة هذه الاختبارات.

طريقة واحدة لمعرفة هذا من خلال اختبار الطفرة. تتيح لك هذه الأداة ، التي تجري تغييرات طفيفة على الكود المصدري وإعادة تشغيل الاختبارات بعد ذلك ، تحديد الاختبارات عديمة الفائدة وتغطية ذات جودة منخفضة.

في Badoo PHP Meetup في مارس ، تحدثت عن كيفية تنظيم اختبار تحولي لرمز PHP والمشاكل التي قد تواجهها. الفيديو متاح هنا ، وللنسخة النصية ، مرحبًا بك في cat.



ما هو اختبار الطفرة


لشرح ما أعنيه ، سأريك بعض الأمثلة. إنها بسيطة ومبالغ فيها في أماكن وقد تبدو واضحة (على الرغم من أن الأمثلة الحقيقية عادة ما تكون معقدة للغاية ولا يمكن رؤيتها بأعينهم).

النظر في الموقف: لدينا وظيفة أولية تدعي أنها شخص بالغ ، وهناك اختبار يختبرها. يشتمل الاختبار على مزود بيانات ، أي أنه يختبر حالتين: العمر 17 عامًا و 19 عامًا. أعتقد أنه من الواضح لكثير منكم أن Adult لديه تغطية بنسبة 100 ٪. الخط الوحيد. يتم تنفيذها عن طريق اختبار. كل شئ رائع



لكن الفحص الدقيق يكشف أن مزودنا مكتوب بشكل سيء ولا يختبر شروط الحدود: سن 18 عامًا كشرط حدودي لم يتم اختباره. يمكنك استبدال العلامة>> ، ولن يختبر الاختبار هذا التغيير.

مثال آخر ، قليلا أكثر تعقيدا. هناك وظيفة تقوم ببناء بعض الأشياء البسيطة التي تحتوي على أدوات تسوية وحروف. لدينا ثلاثة حقول نضعها ، وهناك اختبار يتحقق من أن وظيفة buildPromoBlock تجمع الكائن الذي نتوقعه.



إذا نظرت عن كثب ، لدينا أيضًا setSomething ، والذي يضبط بعض الخصائص على true. لكن في الاختبار ليس لدينا مثل هذا التأكيد. وهذا يعني أنه يمكننا إزالة هذا السطر من buildPromoBlock - ولن يجرب اختبارنا هذا التغيير. في الوقت نفسه ، لدينا تغطية 100 ٪ في وظيفة buildPromoBlock ، لأنه تم تنفيذ جميع الخطوط الثلاثة خلال الاختبار.

يقودنا هذان المثالان إلى اختبار التحور.

قبل تفكيك الخوارزمية ، سأقدم تعريفًا قصيرًا. اختبار الطفرة عبارة عن آلية تسمح لنا ، بإجراء تغييرات طفيفة على الكود ، بتقليد تصرفات الشر بينوكيو أو فاسيا الصغير ، الذي أتى وبدأ في كسرها عن قصد ، واستبدال العلامات> بعلامة <، = بواسطة! = ، وهكذا. لكل تغيير نقوم به لأغراض جيدة ، فإننا نجري اختبارات من شأنها أن تغطي الصف الذي تم تغييره.

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

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

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



في هذه الحالة ، لا يتم إجراء الطفرات بشكل عشوائي ، ولكن وفقًا لقواعد معينة. استجابة اختبار الطفرة عاطفية. بغض النظر عن عدد المرات التي نجري فيها اختبارات التحويل على نفس الكود ، فإنها تنتج نفس النتائج.

آخر شيء نقوم به هو إجراء الاختبارات التي تغطي الخط المتحور. أخرجه من التغطية. هناك أدوات غير مثالية تقود جميع الاختبارات. ولكن أداة جيدة لن تؤدي إلا إلى إبعاد ما هو مطلوب.

بعد ذلك ، نقوم بتقييم النتيجة. سقطت الاختبارات - ثم كل شيء على ما يرام. إذا لم تسقط ، فهي ليست فعالة للغاية.

المقاييس


ما المقاييس التي يقدمها اختبار الطفرة؟ إنه يضيف ثلاثة رموز إضافية إلى التغطية البرمجية ، والتي سنتحدث عنها الآن.

لكن أولاً ، دعنا نحلل المصطلحات.



هناك مفهوم المسوخ القاتل: هذه هي المسوخات التي تم اختبارها من قبل "المسامير" (أي تم القبض عليها).



هناك مفهوم المسخ الهرب (المسوخ الباقي على قيد الحياة). هؤلاء هم المسوخون الذين تمكنوا من تجنب العقوبة (أي أن الاختبارات لم تصاب بهم).



وهناك مفاهيم غطيت متحولة - متحولة غطتها الاختبارات ، متحولة غير مكشوفة ، والتي لا يغطيها أي اختبار على الإطلاق (على سبيل المثال ، لدينا رمز ، وله منطق عمل ، ويمكننا تغييره ، ولكن ليس اختبار واحد لا تحقق من وجود تغييرات).

المؤشر الرئيسي الذي يعطينا اختبار الطفرة هو MSI (مؤشر درجة الطفرة) ، نسبة عدد المسوخ القاتل إلى إجمالي عددهم.

المؤشر الثاني هو تغطية رمز الطفرة. إنه مجرد نوعي وليس كمي ، لأنه يوضح مقدار منطق العمل الذي يمكنك كسره والقيام به على أساس منتظم ، يتم اكتشاف اختباراتنا.

ويغطي القياس الأخير MSI ، أي MSI ليونة. في هذه الحالة ، نحسب MSI فقط لتلك المسوخات التي كانت مغطاة بالاختبارات.

قضايا اختبار الطفرة


لماذا سمع أقل من نصف المبرمجين عن هذه الأداة؟ لماذا لا تستخدم في كل مكان؟

سرعة منخفضة


المشكلة الأولى (واحدة من أهمها) هي سرعة اختبار الطفرة. في الكود ، إذا كان لدينا العشرات من مشغلي الطفرات ، حتى بالنسبة للفئة الأبسط ، يمكننا توليد مئات الطفرات. لكل طفرة ، سوف تحتاج إلى إجراء الاختبارات. إذا كان لدينا ، على سبيل المثال ، 5000 اختبار وحدة تستمر لمدة عشر دقائق ، فقد يستغرق الاختبار التحولي ساعات.

ما الذي يمكن عمله لتحقيق هذا المستوى؟ تشغيل الاختبارات بالتوازي ، في عدة خيوط. تيارات رمي ​​في العديد من السيارات. إنه يعمل.

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

الشيء التالي الذي يمكنك القيام به هو ضبط الطفرة. نظرًا لأنه يمكن تغيير معاملات الطفرة ، يمكنك تعيين قواعد معينة تعمل من خلالها ، ثم يمكنك إيقاف بعض الطفرات إذا أدت إلى حدوث مشكلات عن قصد.

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

المسوخ لا نهاية لها


المشكلة الثانية التي يمكن أن تنشأ مع اختبارات التحور هي ما يسمى المسوخ الذي لا نهاية له. على سبيل المثال ، هناك رمز بسيط ، بسيط للحلقة:



إذا استبدلت i ++ بـ i-- ، فستتحول الدورة إلى لانهائي. سوف تتمسك الشفرة لفترة طويلة. وغالبا ما يولد الاختبار التحولي مثل هذه الطفرات.

أول شيء يمكنك القيام به هو ضبط الطفرة. من الواضح أن تغيير i ++ إلى i-- في حلقة for a فكرة سيئة جدًا: في 99٪ من الحالات ، سينتهي بنا المطاف بحلقة لانهائية. لذلك ، لقد حرمنا من القيام بذلك في أداتنا.

الشيء الثاني والأهم الذي يحميك من مثل هذه المشاكل هو مهلة التشغيل. على سبيل المثال ، يمتلك نفس PHPUnit القدرة على إكمال اختبار مهلة بغض النظر عن مكانه. PHPUnit من خلال PCNTL توقف عمليات الاسترجاعات ويحسب الوقت نفسه. إذا فشل الاختبار لفترة معينة ، فسيقوم ببساطة بتثبيته ويعد مثل هذه الحالة بمثابة طفرة قاتلة ، لأن الرمز الذي تولد الطفرات يتم فحصه فعليًا بواسطة الاختبار ، الذي يصيب المشكلة حقًا ، مما يشير إلى أن الشفرة أصبحت غير صالحة للعمل.

المسوخ متطابقة


هذه المشكلة موجودة في نظرية اختبار الطفرة. في الممارسة العملية ، لا يواجهونها كثيرًا ، ولكن عليك أن تعرف ذلك.

النظر في مثال كلاسيكي يوضح ذلك. لدينا ضرب من المتغير A ب -1 وقسم A ب -1. في الحالة العامة ، تؤدي هذه العمليات إلى نفس النتيجة. نغير علامة A. وفقًا لذلك ، لدينا طفرة تسمح بتغيير علامتين فيما بينهما. لا ينتهك منطق البرنامج من خلال مثل هذه الطفرة. الاختبارات ويجب ألا يصاب بها ، لا ينبغي أن تقع. بسبب هذه الطفرات متطابقة ، تنشأ بعض الصعوبات.

لا يوجد حل عالمي - الكل يحل هذه المشكلة بطريقته الخاصة. ربما نوعا من نظام تسجيل متحولة سوف تساعد. نحن في Badoo نفكر في شيء مماثل الآن ، وسنحاكيهم.

هذه هي النظرية. ماذا عن PHP؟


هناك نوعان من الأدوات المعروفة للاختبار الطفري: Humbug and Infection. عندما كنت أعد المقال ، أردت أن أتحدث عن أيهما أفضل والتوصل إلى استنتاج مفاده أن هذا هو العدوى.
لكن عندما ذهبت إلى صفحة Humbug ، رأيت ما يلي هناك: أعلن Humbug أنه عفا عليه الزمن لصالح العدوى. لذلك ، تبين أن جزءًا من مقالتي لا معنى له. لذلك العدوى هي أداة جيدة حقا. يجب أن أقول بفضل borNfree من مينسك ، الذي أنشأها. انه حقا يعمل بارد. يمكنك أخذها مباشرة من الصندوق ، ووضعها من خلال الملحن وبدء تشغيلها.

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

هناك خيار للتغذية الجاهزة ، لكن في تنسيق PHPUnit ، هناك مجموعة من ملفات XML. إلى جانب حقيقة أنها تحتوي على معلومات قيمة ، فإنها تسحب مجموعة من الهياكل وبعض الأقواس وغيرها من الأشياء. لقد اعتقدت أن تغطية الشفرة الخاصة بنا عمومًا سوف تزن حوالي 30 جيجابايت ، ونحن بحاجة إلى سحبها عبر جميع الأجهزة السحابية ، التي يتم قراءتها باستمرار من القرص. بشكل عام ، والفكرة هي ذلك.
كانت المشكلة الثانية أكثر أهمية. لدينا مكتبة SoftMocks رائعة. يتيح لنا التعامل مع الشفرة القديمة ، والتي يصعب اختبارها ، ونكتب الاختبارات بنجاح. نحن نستخدمها بنشاط ولن نرفضها في المستقبل القريب ، على الرغم من أننا نكتب رمزًا جديدًا حتى لا نحتاج إلى SoftMocks. لذلك ، هذه المكتبة لا تتوافق مع Infection ، لأنها تستخدم نفس الطريقة تقريبًا في تغيير التغييرات.

كيف تعمل SoftMocks؟ إنهم يعترضون إدخالات الملفات ويستبدلوها بأخرى معدلة ، أي بدلاً من تنفيذ الفئة أ ، تقوم SoftMocks بإنشاء الفئة أ في مكان مختلف وتوصيل أخرى بدلاً من النسخة الأصلية. تعمل العدوى بنفس الطريقة تمامًا ، حيث تعمل فقط من خلال stream_wrapper_register () ، الذي يقوم بنفس الشيء ، ولكن على مستوى النظام. نتيجة لذلك ، يمكن أن تعمل SoftMocks أو Infection لنا. نظرًا لأن SoftMocks ضرورية لاختباراتنا ، فمن الصعب للغاية جعل هاتين الأداتين أصدقاء. قد يكون هذا ممكنًا ، لكن في هذه الحالة ، ندخل في العدوى لدرجة أن معنى هذه التغييرات قد فقد ببساطة.

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

كيف نستخدم هذا؟

الأول هو التشغيل اليدوي. هذا هو أول شيء فعله. يتم التحقق يدويًا من جميع الاختبارات التي تكتبها عن طريق اختبار الطفرة. يبدو شيء مثل هذا:



لقد أجريت اختبار طفرة لبعض الملفات. حصلت على النتيجة: 16 المسوخ. ومن بين هؤلاء ، قُتل 15 شخصًا عن طريق الاختبارات ، وسقط واحد بسبب خطأ. لم أقل أن الطفرات يمكن أن تسبب الوفيات. يمكننا بسهولة تغيير شيء ما: جعل نوع الإرجاع غير صالح أو شيء آخر. هذا ممكن ، فهو يعتبر طفرة قاتلة ، لأن اختبارنا سيبدأ في الانخفاض.

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

الشيء الثاني الذي نستخدمه هو تقرير السيد. مرة واحدة يوميًا ، في الليل ، عندما تكون البنية التحتية للتطوير لدينا في وضع الخمول ، نقوم بإنشاء تقرير تغطية الكود. بعد ذلك ، نجعل نفس تقرير اختبار الطفرة. يبدو مثل هذا:



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

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



على سبيل المثال ، قمت بتشغيل ملفين وحصلت على هذه النتيجة. في الأستاذ كان لدي 548 مسوخًا ، قتل 400. ووفقًا لملف آخر - 147 مقابل 63. في فرعي ، زاد عدد المسوخات في كلتا الحالتين. لكن في الملف الأول ، تم مسمر المسخ ، وفي الثاني ، هرب. وبطبيعة الحال ، انخفض مؤشر MSI. مثل هذا الشيء يسمح حتى للأشخاص الذين لا يرغبون في تضييع الوقت في إجراء الاختبارات التحريرية بأيديهم ، ومعرفة ما قاموا به بشكل أسوأ ، والانتباه إليه (تمامًا كما يفعل المراجعون في عملية مراجعة الكود).

النتائج


لا يزال من الصعب إعطاء أي أرقام: لم يكن لدينا أي مؤشر ، لقد ظهر الآن ، لكن لا يوجد شيء للمقارنة.

أستطيع أن أقول أن الاختبار التحولي يعطي من حيث التأثير النفسي. إذا بدأت في تشغيل اختباراتك من خلال اختبار التحور ، فأنت تبدأ بشكل لا إرادي في كتابة اختبارات أفضل ، وتؤدي اختبارات جودة الكتابة حتماً إلى تغيير في الطريقة التي تكتب بها الشفرة - تبدأ التفكير في أنك بحاجة إلى تغطية جميع الحالات التي يمكنك كسرها ، وتبدأ تشغيلها هيكل أفضل ، وجعلها أكثر قابلية للاختبار.

هذا رأي شخصي حصري. لكن بعض زملائي قدموا نفس الملاحظات تقريبًا: عندما بدأوا في استخدام الاختبارات التحريرية بشكل مستمر في عملهم ، بدأوا في كتابة الاختبارات بشكل أفضل ، وقال كثيرون إنهم بدأوا في كتابة الكود بشكل أفضل.

النتائج


تغطية الشفرة هي مقياس مهم يجب مراقبته. لكن هذا المؤشر لا يضمن أي شيء: هذا لا يعني أنك آمن.

يمكن أن يساعد اختبار التحوّل في جعل اختبارات الوحدة الخاصة بك أفضل ، وتتبع تغطية الشفرة منطقيًا. هناك بالفعل أداة لـ PHP ، لذلك إذا كان لديك مشروع صغير دون مشاكل ، فاستغل وجرب اليوم.

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

Source: https://habr.com/ru/post/ar462709/


All Articles