لماذا تعتبر اختبارات E2E "وضع الإله" لمطور iOS؟
\n\nتخيل: لقد كتبت كود Swift مثاليًا، كل شيء يتم تجميعه، واختبارات الوحدة خضراء. تقوم بإصدار الإصدار - وبعد ساعة تظهر الأخطاء: زر "شراء" لا يعمل على iPhone 14 Pro، وشاشة الدخول على iPad Mini تبدو كلوحة تجريدية. هل هذا مألوف؟ هنا يأتي دور اختبار E2E لتطبيقات Swift - الطريقة الوحيدة للتأكد من أن تطبيقك يعمل كالساعة في أيدي مستخدم حقيقي.
\n\nتحاكي اختبارات End-to-End (E2E) السيناريو الكامل للمستخدم: من تشغيل التطبيق إلى إكمال الإجراء المستهدف (شراء، تسجيل، إرسال نموذج). في هذه المقالة، سنشرح كيفية إعداد XCUITest Swift بحيث لا تفشل اختباراتك لأسباب تافهة، ولا يتحول خط أنابيب CI/CD إلى جحيم.
\n\nما الذي ستتعلمه:
\n- \n
- كيفية كتابة اختبارات UI مستقرة باستخدام XCUITest \n
- نموذج Page Object لـ Swift - مثال عملي \n
- معالجة العمليات غير المتزامنة والانتظارات \n
- دمج اختبارات E2E في CI/CD (GitHub Actions + Fastlane) \n
- أهم 3 أخطاء للمبتدئين وكيفية تجنبها \n
الأدوات: ما سنحتاجه
\n\nلـ أتمتة اختبار Swift سنستخدم ثلاث ركائز:
\n- \n
- XCUITest - إطار عمل من Apple، مدمج في XCTest. يسمح بمحاكاة الإيماءات والتحقق من حالات عناصر UI. \n
- Xcode Test Plan - لتكوين مخططات الاختبار (لغات مختلفة، اتجاهات، بيانات). \n
- Fastlane + GitHub Actions - لتشغيل الاختبارات على CI. \n
بناء جملة XCUITest الأساسي بسيط: يمكنك الوصول إلى التطبيق عبر XCUIApplication()، والبحث عن العناصر عبر buttons["Login"] أو textFields["email"] ومحاكاة الإجراءات.
كتابة أول اختبار E2E: سيناريو تسجيل الدخول
\n\nلنفترض أن لدينا شاشة دخول تحتوي على حقلي البريد الإلكتروني وكلمة المرور وزر "تسجيل الدخول". الهدف: التحقق من أنه عند إدخال بيانات صحيحة، ينتقل المستخدم إلى الشاشة الرئيسية.
\n\nimport XCTest\n\nclass LoginE2ETests: XCTestCase {\n \n let app = XCUIApplication()\n \n override func setUpWithError() throws {\n continueAfterFailure = false\n app.launch()\n }\n \n func testSuccessfulLogin() throws {\n // 1. نجد العناصر\n let emailField = app.textFields["emailTextField"]\n let passwordField = app.secureTextFields["passwordTextField"]\n let loginButton = app.buttons["signInButton"]\n \n // 2. إجراءات المستخدم\n emailField.tap()\n emailField.typeText("user@example.com")\n \n passwordField.tap()\n passwordField.typeText("ValidPassword123")\n \n loginButton.tap()\n \n // 3. نتحقق من النتيجة\n let dashboard = app.staticTexts["dashboardTitle"]\n XCTAssertTrue(dashboard.waitForExistence(timeout: 5))\n }\n}\n\nنقطة مهمة: استخدم waitForExistence(timeout:) - بدون ذلك قد يفشل الاختبار إذا تأخرت الرسوم المتحركة أو طلب الشبكة. هذا هو أساس اختبار E2E لتطبيقات Swift.
لماذا قد يفشل هذا الكود؟
\n- \n
- قد لا يكون حقل الإدخال هو المستجيب الأول - أضف
.tap()قبلtypeText. \n - قد يكون الزر مغطى بلوحة المفاتيح - استخدم
app.keyboards.buttons["Return"].tap(). \n - قد تختلف تسميات النصوص حسب الترجمة - استخدم دائمًا معرف Accessibility Identifier. \n
نموذج Page Object (POM) لـ Swift: جعل الاختبارات نظيفة
\n\nعندما يكبر التطبيق، تتحول الاختبارات إلى فوضى. الحل هو نموذج Page Object. نقوم بإنشاء فئات منفصلة لكل شاشة، تغلف منطق البحث عن العناصر والإجراءات.
\n\nمثال لشاشة تسجيل الدخول:
\n\n// Lo