الاختبار القائم على الخصائص في Swift: عندما تفشل الاختبارات التقليدية
\n\nهل سبق لك أن كتبت اختبار وحدة ينجح "بامتياز"، ثم يتعطل التطبيق على بيانات حقيقية؟ مألوف، أليس كذلك؟ الاختبارات التقليدية القائمة على الأمثلة (عندما نتحقق من مدخل ومخرج محددين) جيدة، لكن لديها عيب أساسي: فهي تتحقق فقط مما نبتكره نحن بأنفسنا. ماذا لو كان الخطأ يعيش على حدود القيم المسموح بها، أو في تركيبة لم تخطر على بالنا ببساطة؟
\n\nهنا يظهر على الساحة الاختبار القائم على الخصائص في Swift. إنه نهج تصف فيه ليس حالات محددة، بل خصائص (properties) يجب أن تكون صحيحة لـ أي بيانات إدخال. ويقوم إطار عمل خاص (مثل SwiftCheck) بتوليد مئات القيم العشوائية والتحقق مما إذا كانت خاصيتك قد انتهكت. إذا تم العثور على خطأ، يقوم الإطار "بتقليل" بيانات الإدخال - وستحصل على أبسط مثال ينهار فيه كل شيء.
\n\nفي هذه المقالة، سنشرح كيفية دمج الاختبار القائم على الخصائص في Swift في مشروعك، وسنكتب أمثلة كود حقيقية، ونرى أي الأخطاء يمكن اكتشافها بهذا النهج. هيا بنا!
\n\nما هو الاختبار القائم على الخصائص ولماذا هو مطلوب؟
\n\nتخيل أنك تختبر دالة تقوم بترتيب مصفوفة. بدلاً من كتابة 10 اختبارات بمصفوفات مختلفة، تقول: "بالنسبة لأي مصفوفة، بعد الترتيب لا يتغير الطول، وكل عنصر أصغر أو يساوي الذي يليه". هذه هي الخاصية - الثابت الذي يجب أن يتحقق.
\n\nالاختبار القائم على الخصائص في Swift مثالي لـ:
\n- \n
- التحقق من الدوال الرياضية (الجمع، القسمة، التقريب)؛ \n
- اختبار المحللات والمُسلسِلات (التسلسل → إلغاء التسلسل يجب أن يعيد الأصل)؛ \n
- التحقق من منطق الأعمال مع عدد كبير من التركيبات؛ \n
- البحث عن الانحدارات على القيم الحدودية. \n
المكافأة الرئيسية: تجد أخطاء لم تكن تعلم بوجودها. وهذا، كما توافق، أفضل من تأكيد الواضح.
\n\nSwiftCheck - الأداة الرئيسية للاختبار القائم على الخصائص في Swift
\n\nفي عالم Swift، الإطار الأكثر شيوعًا لهذا النهج هو SwiftCheck. إنه ينقل الأفكار من Haskell (مكتبة QuickCheck) ويتكامل بشكل ممتاز مع XCTest.
\n\nللبدء، أضف التبعية في Package.swift:
// swift-tools-version:5.9\nimport PackageDescription\n\nlet package = Package(\n name: "MyProject",\n dependencies: [\n .package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.12.0")\n ],\n targets: [\n .testTarget(\n name: "MyProjectTests",\n dependencies: ["SwiftCheck"]),\n ]\n)\n\n\nالآن يمكنك كتابة الاختبارات. دعنا نشرح بناء الجملة الأساسي.
\n\nأول اختبار قائم على الخصائص: التحقق من الجمع
\n\nلنكتب خاصية بسيطة: "الجمع تجميعي". لأي أعداد صحيحة a + b + c يجب أن يساوي (a + b) + c.
import XCTest\nimport SwiftCheck\n\nclass MathPropertiesTests: XCTestCase {\n func testAdditionIsAssociative() {\n property("الجمع تجميعي") <- forAll { (a: Int, b: Int, c: Int) in\n return (a + b) + c == a + (b + c)\n }\n }\n}\n\n\nنشغل الاختبار - يقوم بتشغيل 100 تركيبة عشوائية (افتراضيًا). إذا فشل واحد على الأقل، سيعرض SwiftCheck القيم المحددة. لاحظ: نحن لا نحدد أي رقم محدد! الاختبار القائم على الخصائص في Swift يولد البيانات بنفسه.
\n\nالتحقق من التحويل العكسي (roundtrip)
\n\nهذا هو النمط الأكثر شيوعًا. لنفترض أن لدينا هيكلًا يتم تسلسله إلى JSON. الخاصية: "بعد التسلسل وإلغاء التسلسل نحصل على نفس الشيء".