اختباراتك تكذب، وأنت لا تعلم حتى بذلك
\n\nتخيل الموقف: لقد كتبت 200 اختبار وحدة، تغطية الكود 95%، CI أخضر، المدير سعيد. تقوم بإصدار التحديث، وبعد أسبوع يتعطل الإنتاج بسبب خطأ كان يجب على الاختبارات أن تكتشفه. هل يبدو هذا مألوفاً؟ مرحباً بك في عالم الثقة الزائفة، حيث يعمل الاختبار الطفري (mutation testing) كمرآة صادقة لرمز Swift الخاص بك.
\n\nفي هذه المقالة، سنشرح ما هو الاختبار الطفري في Swift، وكيف يعمل عملياً، ولماذا يجب على كل مطور iOS إضافته إلى أدواته. بدون نظرية مملة - فقط كود، أمثلة، وحالات واقعية.
\n\nما هو الاختبار الطفري ولماذا نحتاجه؟
\n\nالاختبار الطفري (mutation testing) هو طريقة لتقييم جودة الاختبارات عن طريق إدخال تغييرات صغيرة (طفرات) في الكود الأصلي. تُسمى هذه النسخ المعدلة المتحولين (mutants). إذا لم تكتشف الاختبارات المتحول (أي لم تفشل)، فهذا يعني أن تغطية الاختبار لديك ضعيفة، وأن هذا المتحول قد نجا.
\n\nلماذا هذا مهم؟ المقاييس التقليدية مثل تغطية الكود (code coverage) تظهر فقط النسبة المئوية للكود الذي تم تنفيذه أثناء الاختبارات. لكنها لا تجيب على السؤال الرئيسي: هل تتحقق الاختبارات من صحة المنطق؟ يجيب الاختبار الطفري على هذا السؤال: إذا قمت بتغيير الشرط if a > b إلى if a < b عن طريق الخطأ، هل ستلاحظ اختباراتك ذلك؟
كيف يعمل الاختبار الطفري في Swift: ببساطة
\n\nتبدو العملية كالتالي:
\n\n- \n
- الكود الأصلي - ملف Swift العامل الخاص بك. \n
- توليد المتحولين - يقوم البرنامج تلقائياً بإنشاء إصدارات متعددة من الكود مع خطأ صغير واحد (مثل تغيير
+إلى-). \n - تشغيل الاختبارات - لكل متحول، يتم تشغيل مجموعة كاملة من اختبارات الوحدة. \n
- تحليل النتائج - إذا فشلت الاختبارات، فقد "قُتل" المتحول. إذا نجحت الاختبارات، فقد "نجا" المتحول، وهذه علامة حمراء. \n
نسبة المتحولين المقتولين تُسمى درجة الطفر (mutation score). كلما كانت أعلى، كانت اختباراتك أكثر موثوقية.
\n\nمثال عملي: كتابة الكود والاختبارات في Swift
\n\nلنتخيل أننا نكتب آلة حاسبة بسيطة للخصومات لمتجر إلكتروني في Swift. هذا هو كود الإنتاج الخاص بنا:
\n\n// DiscountCalculator.swift\nstruct DiscountCalculator {\n func calculateDiscount(price: Double, isPremiumUser: Bool) -> Double {\n var discount: Double = 0.0\n \n if price > 100 {\n discount += 0.1 // خصم أساسي 10%\n }\n \n if isPremiumUser {\n discount += 0.05 // 5% إضافية للمستخدمين المميزين\n }\n \n return price * (1 - discount)\n }\n}\n\n\nيبدو كل شيء منطقياً. لنكتب اختبارات الوحدة:
\n\n// DiscountCalculatorTests.swift\nimport XCTest\n\nfinal class DiscountCalculatorTests: XCTestCase {\n let calculator = DiscountCalculator()\n \n func testDiscountForRegularUserLowPrice() {\n let result = calculator.calculateDiscount(price: 50, isPremiumUser: false)\n XCTAssertEqual(result, 50.0, accuracy: 0.01)\n }\n \n func testDiscountForRegularUserHighPrice() {\n let result = calculator.calculateDiscount(price: 200, isPremiumUser: false)\n XCTAssertEqual(result, 180.0, accuracy: 0.01)\n }\n \n func testDiscountForPremiumUserLowPrice() {\n let result = calculator.calculateDiscount(price: 50, isPremiumUser: true)\n XCTAssertEqual(result, 47.5, accuracy: 0.01)\n }\n \n func testDiscountForPremiumUserHighPrice() {\n let result = calculator.calculateDiscount(price: 200, isPremiumUser: true)\n XCTAssertEqual(result, 170.0, accuracy: 0.01)\n }\n}\n\n\nالاختبارات ناجحة. التغطية 100%. لكن دعنا نرى ما سيقوله الاختبار الطفري.
\n