رحلة اختبار E2E الجزء 2: WebdriverIO إلى Cypress

نشرت: 2019-11-21

ملاحظة: هذا منشور من # frontend @ twiliosendgrid. بالنسبة إلى المنشورات الهندسية الأخرى ، توجه إلى قائمة المدونات الفنية.

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

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

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

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

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

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

TLDR: اعتمدنا Cypress على STUI و WebdriverIO وحققنا جميع أهدافنا في كتابة اختبارات E2E قيّمة للتكامل مع CICD. تم نقل الكثير من عملنا والدروس المستفادة من WebdriverIO و STUI بشكل جيد في كيفية استخدامنا واختبارات Cypress اليوم.

جدول المحتويات

الاستكشاف والهبوط على السرو

التبديل من STUI و WebdriverIO إلى Cypress

الخطوة 1: تثبيت التبعيات لـ Cypress

الخطوة 2: البيئة Configs and Scripts

المرور الأول لملفات البيئة والنصوص

تطور البيئة Configs والنصوص

الخطوة 3: تنفيذ اختبارات E2E محليًا

الخطوة 4: إرساء الاختبارات

الخطوة الخامسة: التكامل مع CICD

الخطوة 6: مقارنة Cypress مقابل WebdriverIO / STUI

الخطوة 7: التحجيم إلى فرق الواجهة الأمامية الأخرى

ما نتطلع إليه مع Cypress

تبني السرو في المستقبل

الاستكشاف والهبوط على السرو

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

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

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

كما نقدر أيضًا أطر عمل الاختبار ومكتبات التأكيد وجميع الأدوات الأخرى التي تم اختيارها لنا للحصول على نهج معياري أكثر للاختبارات عبر جميع فرق الواجهة الأمامية لدينا. أدناه ، قدمنا ​​لقطة شاشة للاختلافات بين حل مثل WebdriverIO و Cypress وإذا كنت ترغب في رؤية المزيد من الاختلافات بين حلول Cypress و Selenium ، يمكنك التحقق من وثائقهم حول كيفية عمله.

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

التبديل من STUI و WebdriverIO إلى Cypress

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

  1. تثبيت وإعداد التبعيات للتواصل مع Cypress
  2. إنشاء تكوينات البيئة وأوامر البرامج النصية
  3. تنفيذ اختبارات E2E التي تمر محليًا مقابل بيئات مختلفة
  4. تفريغ الاختبارات
  5. دمج الاختبارات Dockerized مع Buildkite ، مزود CICD الخاص بنا

من أجل تحقيق أهدافنا الثانوية ، أضفنا أيضًا خطوات إضافية لمقارنة Cypress مع حلول السيلينيوم السابقة ولتوسيع نطاق Cypress في نهاية المطاف عبر جميع فرق الواجهة الأمامية في المؤسسة:

6. مقارنة Cypress من حيث تجارب المطورين ، والسرعة ، واستقرار الاختبارات مقابل WebdriverIO و STUI
7. التحجيم إلى فرق الواجهة الأمامية الأخرى

الخطوة 1: تثبيت التبعيات لـ Cypress

للحصول على Cypress وتشغيله بسرعة ، كل ما كان علينا القيام به هو "npm install cypress" في مشاريعنا وبدء تشغيل Cypress لأول مرة حتى يتم وضعه تلقائيًا مع ملف التكوين "cypress.json" ومجلد cypress مع تركيبات المبتدئين والاختبارات وملفات الإعداد الأخرى للأوامر والمكونات الإضافية. لقد قدرنا كيف تم تجميع Cypress مع Mocha باعتباره عداء الاختبار ، و Chai للتأكيدات ، و Chai-jQuery و Sinon-Chai لمزيد من التأكيدات للاستخدام والتسلسل. لم يعد علينا قضاء وقت طويل في البحث عن عداء الاختبار والمراسل والتأكيدات ومكتبات الخدمة التي يجب تثبيتها واستخدامها مقارنةً بالوقت الذي بدأنا فيه باستخدام WebdriverIO أو STUI. أجرينا على الفور بعض الاختبارات التي تم إنشاؤها باستخدام واجهة المستخدم الرسومية Cypress الخاصة بهم واستكشفنا العديد من ميزات تصحيح الأخطاء المتاحة لنا مثل تصحيح أخطاء السفر عبر الزمن ، وملعب المحدد ، ومقاطع الفيديو المسجلة ، ولقطات الشاشة ، وسجلات الأوامر ، وأدوات مطور المتصفح ، وما إلى ذلك.

قمنا أيضًا بإعداده لاحقًا باستخدام Eslint و TypeScript لمزيد من التحقق من النوع الثابت وقواعد التنسيق لاتباعها عند تنفيذ كود اختبار Cypress جديد. في البداية كان لدينا بعض الفواق مع دعم TypeScript وبعض الملفات التي تحتاج إلى أن تكون ملفات JavaScript مثل تلك التي تتمحور حول ملفات المكونات الإضافية ، ولكن بالنسبة للجزء الأكبر تمكنا من كتابة التحقق من غالبية ملفاتنا للاختبارات وكائنات الصفحة والأوامر.

فيما يلي مثال على بنية المجلد التي اتبعتها إحدى فرق الواجهة الأمامية لدينا لتضمين كائنات الصفحة والمكونات الإضافية والأوامر:

الخطوة 2: البيئة Configs and Scripts

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

  • مقابل خادم Webpack dev الذي يعمل على مضيف محلي (مثل http: // localhost: 8000) وسيتم توجيه خادم dev هذا إلى واجهة برمجة تطبيقات بيئة معينة (مثل https://testing.api.com أو https: //staging.api. com) مثل الاختبار أو التدريج.
    لماذا ا؟ نحتاج في بعض الأحيان إلى إجراء تغييرات على تطبيق الويب المحلي الخاص بنا ، مثل إضافة محددات أكثر تحديدًا لاختباراتنا للتفاعل مع العناصر بطريقة أكثر قوة أو كنا في طور تطوير ميزة جديدة ونحتاج إلى ضبط والتحقق من صحة اختبارات الأتمتة الحالية ستمر محليًا مقابل تغييراتنا البرمجية الجديدة. كلما تغير رمز التطبيق ولم نضغط على البيئة المنشورة حتى الآن ، استخدمنا هذا الأمر لإجراء اختباراتنا مقابل تطبيق الويب المحلي الخاص بنا.
  • مقابل تطبيق تم نشره لبيئة معينة (مثل https://testing.app.com أو https://staging.app.com) مثل الاختبار أو التدريج
    لماذا ا؟ في أحيان أخرى ، لا يتغير رمز التطبيق ولكن قد نضطر إلى تغيير رمز الاختبار الخاص بنا لإصلاح بعض التقلبات أو نشعر بالثقة الكافية لإضافة الاختبارات أو حذفها تمامًا دون إجراء أي تغييرات في الواجهة الأمامية. لقد استخدمنا هذا الأمر بكثافة لتحديث الاختبارات أو تصحيحها محليًا مقابل التطبيق المنشور لمحاكاة كيفية عمل اختباراتنا في CICD عن كثب.
  • التشغيل في حاوية Docker مقابل تطبيق تم نشره لبيئة معينة مثل الاختبار أو التدريج
    لماذا ا؟ هذا مخصص لـ CICD حتى نتمكن من تشغيل اختبارات E2E في حاوية Docker على سبيل المثال التطبيق المنشور المرحلي والتأكد من اجتيازها قبل نشر الكود في الإنتاج أو في عمليات الاختبار المجدولة في خط أنابيب مخصص. عند إعداد هذه الأوامر في البداية ، أجرينا الكثير من التجارب والأخطاء لتدوير حاويات Docker بقيم متغيرة للبيئة واختبارها لمعرفة الاختبارات المناسبة التي تم تنفيذها بنجاح قبل ربطها بمزود CICD لدينا ، Buildkite.

المرور الأول لملفات البيئة والنصوص

عندما جربنا أولاً إعداد Cypress ، فعلنا ذلك في الريبو الذي يغطي https://app.sendgrid.com ، وهو تطبيق ويب يتضمن صفحات ميزات مثل مصادقة المرسل ونشاط البريد الإلكتروني والتحقق من البريد الإلكتروني ، وقد شاركنا حتماً اكتشافاتنا وتعلمنا مع الفرق وراء تطبيق الويب الخاص بحملات التسويق ، والذي يشمل مجال https://mc.sendgrid.com. لقد أردنا تشغيل اختبارات E2E الخاصة بنا مقابل بيئة التدريج الخاصة بنا واستخدمنا واجهة سطر أوامر Cypress وخيارات مثل --config أو --env لإنجاز حالات الاستخدام الخاصة بنا.

من أجل تشغيل اختبارات Cypress مقابل تطبيق الويب محليًا على سبيل المثال http://127.0.0.1:8000 أو مقابل عنوان URL للتطبيق المرحلي المنشور ، قمنا بتعديل علامة التكوين baseUrl في الأمر وإضافة متغيرات البيئة الإضافية مثل testEnv للمساعدة تحميل بعض التركيبات أو بيانات الاختبار الخاصة بالبيئة في اختبارات السرو الخاصة بنا. على سبيل المثال ، قد تختلف مفاتيح واجهة برمجة التطبيقات المستخدمة ، وإنشاء المستخدمين ، والموارد الأخرى عبر البيئات. testEnv لتبديل تلك التركيبات أو إضافة منطق شرطي خاص إذا كانت بعض الميزات غير مدعومة في بيئة أو اختلف إعداد الاختبار وسنصل إلى البيئة من خلال مكالمة مثل Cypress.env(“testEnv”) في مواصفاتنا.

قمنا بعد ذلك بتنظيم cypress:open:* لتمثيل فتح واجهة المستخدم الرسومية Cypress لنا لتحديد اختباراتنا للتشغيل من خلال واجهة المستخدم عندما قمنا بالتطوير محليًا و cypress:run:* للإشارة إلى تنفيذ الاختبارات في الوضع بدون رأس ، والذي كان أكثر تفصيلاً للتشغيل في حاوية Docker أثناء CICD. ما سيحدث بعد open أو run سيكون البيئة ، لذا ستقرأ أوامرنا بسهولة مثل npm run cypress:open:localhost:staging لفتح واجهة المستخدم الرسومية وتشغيل الاختبارات ضد خادم Webpack المحلي الذي يشير إلى انطلاق واجهات برمجة التطبيقات أو npm run cypress:run:staging لإجراء الاختبارات في وضع مقطوعة الرأس مقابل تطبيق التدريج المنشور وواجهة برمجة التطبيقات. ظهرت البرامج النصية package.json Cypress على النحو التالي:

تطور البيئة Configs والنصوص

في مشروع آخر ، قمنا بتطوير أوامر وتكوينات Cypress الخاصة بنا للاستفادة من بعض منطق العقدة في ملف cypress cypress/plugins/index.js للحصول على ملف cypress.json الأساسي وملفات التكوين المنفصلة التي يمكن قراءتها بناءً على متغير بيئة يسمى configFile لتحميل ملف تكوين محدد. سيتم بعد ذلك دمج ملفات التكوين التي تم تحميلها مع الملف الأساسي للإشارة في النهاية إلى الخادم المرحلي أو الخادم الخلفي الوهمي.

في حال كنت تتساءل أكثر عن الخادم الخلفي الوهمي ، فقد طورنا خادمًا سريعًا بنقاط نهاية خلفية تقوم ببساطة بإرجاع استجابات مختلفة لبيانات JSON الثابتة ورموز الحالة (مثل 200 ، 4XX ، 5XX) اعتمادًا على معلمات الاستعلام التي تم تمريرها في الطلبات. أدى هذا إلى إلغاء حظر الواجهة الأمامية لمواصلة تطوير تدفقات الصفحة مع استدعاءات شبكة فعلية لخادم الواجهة الخلفية مع استجابات تحاكي الشكل الذي ستبدو عليه واجهة برمجة التطبيقات الفعلية عندما تكون متاحة في المستقبل. يمكننا أيضًا محاكاة مستويات مختلفة من استجابات النجاح والخطأ بسهولة لحالات واجهة المستخدم المختلفة الخاصة بنا والتي سيكون من الصعب إعادة إنتاجها في الإنتاج ، وبما أننا سنجري مكالمات شبكة حتمية ، فإن اختبارات Cypress الخاصة بنا ستكون أقل هشاشة في إطلاق النار من نفس الشبكة الطلبات والردود في كل مرة.

كان لدينا ملف cypress.json أساسي يتضمن الخصائص المشتركة للمهلة العامة ، ومعرف المشروع للاتصال بخدمة Cypress Dashboard Service التي سنتحدث عنها لاحقًا ، وإعدادات أخرى كما هو موضح أدناه:

أنشأنا مجلد config في مجلد cypress للاحتفاظ بكل ملف من ملفات التكوين الخاصة بنا مثل localhostMock.json لتشغيل خادم Webpack dev المحلي الخاص بنا مقابل خادم API وهمية محلي أو staging.json للتشغيل مقابل تطبيق التدريج المنشور وواجهة برمجة التطبيقات. بدت ملفات التكوين هذه التي سيتم تمييزها ودمجها مع التكوين الأساسي كما يلي:

تحتوي ملفات تكوين CICD على ملف JSON أبسط لأننا احتجنا إلى تعيين متغيرات البيئة ديناميكيًا لمراعاة اختلاف عنوان URL الأساسي للواجهة الأمامية لخدمة Docker ومضيفات واجهة برمجة تطبيقات الخادم الوهمي التي سنبحث فيها لاحقًا.

في ملف cypress/plugins/index.js الخاص بنا ، أضفنا منطقًا لقراءة متغير بيئة يسمى configFile set من أمر Cypress الذي سيقرأ في النهاية الملف المقابل في مجلد config ودمجه مع cypress.json الأساسي كما يلي:

من أجل كتابة أوامر Cypress المعقولة مع متغيرات البيئة المحددة لحالات الاستخدام الخاصة بنا ، استفدنا من Makefile الذي يشبه ما يلي:

من خلال هذه الأوامر الموضوعة بدقة في ملف Makefile ، يمكننا بسرعة القيام بأشياء مثل make cypress_open_staging أو make cypress_run_staging في البرامج النصية الخاصة بـ `package.json` npm.

من قبل ، اعتدنا على وضع بعض الأوامر في سطر طويل واحد يصعب تعديله بدون أخطاء. لحسن الحظ ، ساعد Makefile في نشر الأشياء بشكل أفضل بكثير من خلال الاستيفاء المقروء لمتغيرات البيئة في أوامر Cypress عبر أسطر متعددة. يمكننا تعيين أو تصدير متغيرات البيئة بسرعة مثل configFile الذي سيتم تحميل ملف تهيئة البيئة له ، أو BASE_URL لزيارة صفحاتنا ، أو API_HOST الواجهة الخلفية المختلفة ، أو SPECS لتحديد الاختبارات التي يجب تشغيلها قبل بدء تشغيل أي من أوامر Makefile.

استخدمنا أيضًا أوامر Makefile لنصوص npm وأوامر Docker الأخرى مثل إنشاء أصول Webpack الخاصة بنا أو تثبيت التبعيات أو تشغيل الأوامر بشكل متزامن مع الآخرين. سنقوم أخيرًا بترجمة بعض أوامر Makefile إلى قسم البرامج النصية package.json ، على الرغم من أن هذا لم يكن ضروريًا إذا أراد شخص ما استخدام Makefile فقط ، وسيبدو كما يلي:

لقد استبعدنا عن قصد الكثير من أوامر Cypress CICD نظرًا لأنها لم تكن أوامر يمكن استخدامها في التطوير اليومي وحافظنا على حزمة package.json أكثر انسيابية نتيجة لذلك. الأهم من ذلك ، يمكننا أن نرى في لمحة سريعة جميع أوامر Cypress المتعلقة بالخادم الوهمي وخادم Webpack dev المحلي مقابل البيئات المرحلية وأي منها "يفتح" واجهة المستخدم الرسومية بدلاً من "التشغيل" في وضع بدون رأس.

الخطوة 3: تنفيذ اختبارات E2E محليًا

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

على سبيل المثال ، ظلت الخطوات العامة للاختبارات كما هي من منظور المستخدم النهائي ، لذلك تضمن عمل التحويل تعيين WebdriverIO أو STUI API إلى Cypress API بالطرق التالية:

  • ظهرت الكثير من الأوامر بشكل أساسي وعملت بشكل مشابه للنقطة التي كنا نستبدل فيها $ أو browser بـ cy أو Cypress في زيارة صفحة من خلال $(“.button”).click() للانتقال إلى cy.get(“.button”).click() ، browser.url() إلى cy.visit() ، أو $(“.input”).setValue() إلى cy.get(“.input”).type()
  • يتحول استخدام $ أو $$ عادةً إلى cy.get(...) أو cy.contains(...) على سبيل المثال $$(“.multiple-elements-selector”) أو $(“.single-element-selector”) إلى cy.get(“.any-element-selector”) أو cy.contains(“text”) أو cy.contains(“.any-selector”)
  • إزالة الدخيلة $(“.selector”).waitForVisible(timeoutInMs) ، $(“.selector”).waitUntil(...) ، أو $(“.selector”).waitForExist() المكالمات لصالح السماح لـ Cypress افتراضيًا معالجة عمليات إعادة المحاولة واسترداد العناصر مرارًا وتكرارًا باستخدام cy.get('.selector') و cy.contains(textInElement) . إذا احتجنا إلى مهلة أطول من الافتراضي ، cy.get('.selector', { timeout: longTimeoutInMs }) تمامًا ثم بعد استرداد العنصر ، سنقوم بتسلسل أمر الإجراء التالي للقيام بشيء ما باستخدام العنصر مثل cy.get(“.selector”).click() .
  • أوامر مخصصة مع المتصفح. addCommand('customCommand, () => {})` turned into `Cypress.Commands.add('customCommand', () => {}) ونفذ` `cy.customCommand ()`
  • أدى إجراء طلبات الشبكة للإعداد أو التفكيك من خلال واجهة برمجة التطبيقات باستخدام مكتبة تسمى node-fetch وتغليفها في browser.call(() => return fetch(...)) و / أو browser.waitUntil(...) إجراء طلبات HTTP في خادم Cypress Node من خلال cy.request(endpoint) أو مكون إضافي مخصص قمنا بتعريفه وإجراء مكالمات مثل cy.task(taskToHitAPIOrService) .
  • قبل ذلك ، عندما كان علينا انتظار طلب شبكة مهم ربما ينتهي دون أي تغييرات ملحوظة في واجهة المستخدم ، كان علينا أن نلجأ أحيانًا إلى استخدام browser.pause(timeoutInMs) وانتظر حتى ينتهي الطلب المحدد باستخدام cy.server() و cy.route(“method”, “/endpoint/we/are/waiting/for).as(“endpoint”)`, and `cy.wait(“@endpoint”) قبل بدء الإجراء الذي من شأنه تشغيل الطلب.

بعد ترجمة الكثير من بناء جملة WebdriverIO والأوامر إلى أوامر Cypress ، قدمنا ​​نفس مفهوم وجود كائن صفحة أساسية للوظائف المشتركة المشتركة وكائنات الصفحة الموسعة لكل صفحة طلبناها للاختبارات. فيما يلي مثال على كائن الصفحة الأساسية بوظيفة open() مشتركة يمكن مشاركتها عبر جميع الصفحات.

سيضيف كائن الصفحة الممتدة محصلات لمحددات العناصر ، وينفذ وظيفة open() مع مسار الصفحة الخاص به ، ويوفر أي وظيفة مساعدة كما هو موضح أدناه.

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

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

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

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

لإعادة تعيين الاختبار إلى حالته الأصلية المتوقعة ، سنقوم بتسجيل الدخول إلى مستخدم اختبار مخصص من خلال واجهة برمجة التطبيقات باستخدام أمر cy.login العالمي ، وتعيين ملف تعريف ارتباط للاحتفاظ بتسجيل دخول المستخدم ، وجعل استدعاءات واجهة برمجة التطبيقات ضرورية لإعادة إلى حالة البداية المطلوبة من خلال cy.request(“endpoint”) أو cy.task(“pluginAction”) ، وقم بزيارة الصفحة المصدق عليها التي سعينا إلى اختبارها مباشرةً. بعد ذلك ، سنقوم بأتمتة الخطوات لإنجاز تدفق ميزة المستخدم كما هو موضح في تخطيط الاختبار أدناه.

هل تتذكر الأوامر المخصصة التي تحدثنا عنها لتسجيل الدخول ، cy.login() ، وتسجيل الخروج ، cy.logout() ؟ قمنا بتنفيذها بسهولة في Cypress بهذه الطريقة بحيث تقوم جميع اختباراتنا بتسجيل الدخول إلى المستخدم من خلال API بنفس الطريقة.

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

بدلاً من ذلك ، قمنا بتطوير مكونات Cypress الإضافية من خلال cy.task(“pluginAction”) الخاصة بهم لاستخدام بعض المكتبات داخل خادم Cypress Node للاتصال بعميل / صندوق وارد IMAP للبريد الإلكتروني التجريبي مثل SquirrelMail للتحقق من مطابقة رسائل البريد الإلكتروني في صندوق الوارد بعد المطالبة بإجراء في واجهة المستخدم ولمتابعة روابط إعادة التوجيه من رسائل البريد الإلكتروني هذه مرة أخرى إلى مجال تطبيق الويب الخاص بنا للتحقق من ظهور صفحات تنزيل معينة واستكمال تدفق العملاء بالكامل بشكل فعال. قمنا بتنفيذ المكونات الإضافية التي من شأنها انتظار وصول رسائل البريد الإلكتروني إلى صندوق الوارد الخاص بـ SquirrelMail مع إعطاء سطور معينة للموضوع ، وحذف رسائل البريد الإلكتروني ، وإرسال رسائل البريد الإلكتروني ، وتشغيل أحداث البريد الإلكتروني ، وخدمات الاستقصاءات الخلفية ، والقيام بعمليات إعداد وتفكيك أكثر فائدة من خلال واجهة برمجة التطبيقات (API) لاستخدامها في اختباراتنا.

لتوفير المزيد من المعلومات حول ما اختبرناه بالفعل باستخدام Cypress ، قمنا بتغطية عدد كبير من الحالات عالية القيمة مثل هذه:

  • الفحوصات الصحية لجميع صفحاتنا المعروفة أيضًا بجولة في التطبيق - أردنا التأكد من أن الصفحات المحملة ببعض المحتوى تتسبب في بعض الأحيان في بعض خدمات الواجهة الخلفية أو تعطل استضافة الواجهة الأمامية. نوصي أيضًا بإجراء هذه الاختبارات أولاً لبناء ذاكرة العضلات الذهنية لبناء كائنات صفحة باستخدام محددات ووظائف مساعدة وللحصول على اختبارات عمل سريعة تعمل في بيئة.
  • عمليات CRUD على الصفحة - سنقوم بإعادة ضبط الاختبارات وفقًا لذلك من خلال واجهة برمجة التطبيقات دائمًا ثم نختبر على وجه التحديد الإنشاء أو القراءة أو التحديث أو الحذف في واجهة المستخدم. على سبيل المثال ، إذا اختبرنا قدرتنا على إنشاء مصادقة المجال من خلال واجهة المستخدم ، بغض النظر عن كيفية انتهاء آخر اختبار تشغيل ، نحتاج إلى التأكد من حذف النطاق الذي كنا سننشئه من خلال واجهة المستخدم في البداية من خلال واجهة برمجة التطبيقات قبل المتابعة مع خطوات واجهة المستخدم التلقائية لإنشاء المجال وتجنب الاصطدامات. إذا اختبرنا قدرتنا على حذف القمع من خلال واجهة المستخدم ، فقد تأكدنا من إنشاء القمع من خلال واجهة برمجة التطبيقات أولاً ثم تابع الخطوات.
  • اختبار عوامل تصفية البحث على الصفحة - اختبرنا إعداد مجموعة من عوامل تصفية البحث المتقدم باستخدام نشاط البريد الإلكتروني وزيارة الصفحة مع معلمات الاستعلام للتأكد من ملء المرشحات تلقائيًا. أضفنا أيضًا البيانات من خلال واجهة برمجة التطبيقات للتحقق من صحة البريد الإلكتروني وأطلقنا مرة أخرى عوامل تصفية بحث مختلفة وتحققنا من صحة الجدول المطابق لفلاتر البحث في تلك الصفحة.
  • وصول مستخدمين مختلفين - في Twilio SendGrid ، لدينا حسابات الوالدين التي يمكن أن يكون لها زملاء في الفريق بنطاقات مختلفة أو أذونات وصول أو مستخدمين فرعيين تحتها والذين لديهم أيضًا درجات متفاوتة من الوصول ويتصرفون بشكل مشابه إلى حد ما لحساب الوالدين. قد يرى زملاء الفريق الذين يتمتعون بالقراءة فقط مقابل وصول المسؤول لصفحات معينة والمستخدمين الفرعيين أشياء معينة على الصفحة أو لا يرونها ، مما يجعل من السهل أتمتة تسجيل الدخول إلى هذه الأنواع من المستخدمين والتحقق مما يرونه أو لا يرونه في اختبارات Cypress.
  • حزم المستخدم المختلفة - يمكن لمستخدمينا أيضًا أن يختلفوا في أنواع الحزم المجانية إلى المدفوعة مثل Essentials و Pro و Premier ويمكن لتلك الحزم رؤية أو عدم رؤية أشياء معينة على الصفحة أيضًا. كنا نسجل الدخول إلى المستخدمين الذين لديهم حزم مختلفة ونتحقق بسرعة من الميزات أو النسخ أو الصفحات التي تمكن المستخدمون من الوصول إليها في اختبارات Cypress.

الخطوة 4: إرساء الاختبارات

عند تنفيذ كل خطوة في خط أنابيب Buildkite على جهاز AWS جديد في السحابة ، لا يمكننا ببساطة استدعاء npm run cypress:run:staging لأن هذه الأجهزة لا تحتوي على عقدة أو متصفحات أو كود تطبيقنا أو أي تبعيات أخرى لتشغيل Cypress فعليًا الاختبارات. عندما أعددنا WebdriverIO من قبل ، احتجنا إلى تجميع ثلاث خدمات منفصلة في ملف Docker Compose حتى تعمل خدمات Selenium و Chrome وكود التطبيق معًا لتشغيل الاختبارات.

مع Cypress ، كان الأمر أكثر وضوحًا لأننا طلبنا فقط صورة Docker لقاعدة Cypress ، cypress/base ، لإعداد البيئة في Dockerfile وخدمة واحدة فقط في ملف docker docker-compose.yml compose.yml مع كود التطبيق الخاص بنا لتشغيل Cypress الاختبارات. سننتقل إلى طريقة واحدة للقيام بذلك حيث توجد صور Cypress Docker أخرى لاستخدامها وطرق أخرى لإعداد اختبارات Cypress في Docker. نحن نشجعك على إلقاء نظرة على وثائق Cypress للحصول على بديل

لإحضار خدمة مع جميع تطبيقاتنا وكود الاختبار الضروري لتشغيل اختبارات Cypress ، قمنا بصياغة Dockerfile يسمى Dockerfile.cypress بتثبيت جميع node_modules ونسخ الكود إلى دليل عمل الصورة في بيئة Node. سيتم استخدام هذا من خلال خدمة Cypress Docker cypress الخاصة بنا وقد حققنا إعداد Dockerfile بالطريقة التالية:

باستخدام Dockerfile.cypress هذا ، يمكننا دمج Cypress لتشغيل المواصفات المحددة مقابل واجهة برمجة تطبيقات بيئة معينة والتطبيق المنشور من خلال خدمة Docker Compose واحدة تسمى cypress . كل ما كان علينا فعله هو إقحام بعض متغيرات البيئة مثل SPECS و BASE_URL لتشغيل اختبارات Cypress المحددة مقابل عنوان URL أساسي معين من خلال npm run cypress:run:cicd:staging command الذي يشبه هذا ، ”cypress:run:cicd:staging”: “cypress run --record --key --config baseUrl=$BASE_URL --env testEnv=staging” .

سيتم تعيين متغيرات البيئة هذه من خلال ملفات إعدادات / تكوين خط أنابيب Buildkite أو يتم تصديرها ديناميكيًا عند تشغيل اختبارات Cypress للتشغيل من خط أنابيب النشر الخاص بنا. بدا أحد الأمثلة على ملف docker-compose.cypress.yml مشابهًا لهذا:

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

بالإضافة إلى ذلك ، ستلاحظ أيضًا مرور BUILDKITE_BUILD_ID ، والذي يأتي مجانًا إلى جانب متغيرات بيئة Buildkite الأخرى لكل تصميم نبدأه ، وعلامة ci-build-id . يتيح ذلك ميزة موازاة Cypress وعندما نضع عددًا معينًا من الآلات المخصصة لاختبارات Cypress ، فسوف يعرف تلقائيًا بطريقة سحرية كيفية تدوير تلك الآلات وفصل اختباراتنا للتشغيل عبر جميع عقد الماكينة لتحسين وتسريع اختبارنا مرات التشغيل.

أخيرًا ، استفدنا أيضًا من زيادة الحجم وميزة القطع الأثرية في Buildkite. نقوم بتحميل مقاطع الفيديو ولقطات الشاشة ليتم الوصول إليها مباشرة من خلال علامة تبويب Buildkite UI "Artifacts" في حالة نفاد تسجيلات الاختبار المخصصة المدفوعة للشهر أو بطريقة ما لا يمكن الوصول إلى خدمة Dashboard Service. عندما يقوم أحد بتشغيل الأمر Cypress "run" في وضع بدون رأس ، يوجد إخراج في مجلدات cypress/videos ومجلدات cypress/screenshots لمراجعتها محليًا ونقوم ببساطة بتركيب هذه المجلدات وتحميلها إلى Buildkite لنا باعتبارها آمنة من الفشل.

الخطوة الخامسة: التكامل مع CICD

بمجرد حصولنا على اختبارات Cypress للتشغيل بنجاح في حاوية Docker مقابل بيئات مختلفة ، بدأنا في الاندماج مع Buildkite ، مزود CICD الخاص بنا. قدمت Buildkite طرقًا لتنفيذ الخطوات في ملف .yml على أجهزة AWS الخاصة بنا مع البرامج النصية Bash ومتغيرات البيئة المحددة إما في الكود أو من خلال إعدادات خط أنابيب Buildkite في repo في واجهة مستخدم الويب. سمحت لنا Buildkite أيضًا بتشغيل خط أنابيب الاختبار هذا من خط أنابيب النشر الرئيسي لدينا مع متغيرات البيئة المصدرة وسنعيد استخدام خطوات الاختبار هذه لأنابيب الاختبار المعزولة الأخرى التي ستعمل وفقًا لجدول زمني لمراقبة ضمان الجودة لدينا والنظر إليها.

على مستوى عالٍ ، شاركت خطوط أنابيب Buildkite للاختبار الخاصة بنا لـ Cypress وكذلك خطوط أنابيب WebdriverIO السابقة الخاصة بنا الخطوات المماثلة التالية:

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

فيما يلي مثال لملف pipeline.cypress.yml الذي يوضح إعداد صور Docker في خطوة "Build Cypress Docker Image" وتشغيل الاختبارات في خطوة "Run Cypress tests":

هناك شيء واحد يجب ملاحظته وهو الخطوة الأولى ، "إنشاء صورة Cypress Docker" ، وكيفية إعداد صورة Docker للاختبار. استخدم الأمر Docker Compose build لإنشاء خدمة cypress بكل كود اختبار التطبيق ووسمها latest متغير بيئة ${VERSION} حتى نتمكن في النهاية من سحب نفس الصورة بالعلامة المناسبة لهذا الإصدار في خطوة مستقبلية. قد يتم تنفيذ كل خطوة على جهاز مختلف في سحابة AWS في مكان ما ، لذلك تحدد العلامات الصورة بشكل فريد لتشغيل Buildkite المحدد. بعد وضع علامات على الصورة ، قمنا بدفع أحدث نسخة من الصورة ذات العلامات إلى سجل Docker الخاص بنا لإعادة استخدامها.

في خطوة "تشغيل اختبارات Cypress" ، نسحب الصورة التي أنشأناها ووسمناها ودفعناها في الخطوة الأولى وبدء تشغيل خدمة Cypress لتنفيذ الاختبارات. استنادًا إلى متغيرات البيئة مثل SPECS و BASE_URL ، سنقوم بتشغيل ملفات اختبار محددة مقابل بيئة تطبيق معينة منشورة لبناء Buildkite المحدد. سيتم تعيين متغيرات البيئة هذه من خلال إعدادات خط أنابيب Buildkite أو سيتم تشغيلها ديناميكيًا من برنامج Bash النصي الذي من شأنه تحليل حقل Buildkite لتحديد مجموعات الاختبار التي سيتم تشغيلها وفي مقابل أي بيئة.

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

سيتم تشغيل الاختبارات التي تم تشغيلها في خط أنابيب منفصل وبعد اتباع رابط "Build # 639" ، سوف يأخذنا إلى خطوات الإنشاء للتشغيل التجريبي المشغل كما يلي:

إعادة استخدام نفس ملف pipeline.cypress.yml Cypress Buildkite المخصصة التي تعمل وفقًا لجدول زمني ، فلدينا تصميمات مثل تلك التي تشغل اختبارات "P1" ، وهي اختبارات E2E ذات الأولوية القصوى ، كما هو موضح في الصورة أدناه:

كل ما يتعين علينا القيام به هو تعيين متغيرات البيئة المناسبة لأشياء مثل المواصفات التي يجب تشغيلها وأي بيئة خلفية يجب الوصول إليها في إعدادات خط أنابيب Buildkite. بعد ذلك ، يمكننا تكوين بناء مجدول لـ Cron ، والذي يوجد أيضًا في إعدادات خط الأنابيب ، لبدء كل عدد معين من الساعات ونحن على ما يرام. We would then create many other separate pipelines for specific feature pages as needed to run on a schedule in a similar way and we would only vary the Cron schedule and environment variables while once again uploading the same `pipeline.cypress.yml` file to execute.

In each of those “Run Cypress tests” steps, we can see the console output with a link to the recorded test run in the paid Dashboard Service, the central place to manage your team's test recordings, billing, and other Cypress stats. Following the Dashboard Service link would take us to a results view for developers and QAs to take a look at the console output, screenshots, video recordings, and other metadata if required such as this:

Step 6: Comparing Cypress vs. WebdriverIO/STUI

After diving into our own custom Ruby Selenium solution in STUI, WebdriverIO, and finally Cypress tests, we recorded our tradeoffs between Cypress and Selenium wrapper solutions.

الايجابيات

  • It's not another Selenium wrapper – Our previous solutions came with a lot of Selenium quirks, bugs, and crashes to work around and resolve, whereas Cypress arrived without the same baggage and troubles to deal with in allowing us full access to the browser.
  • More resilient selectors – We no longer had to explicitly wait for everything like in WebdriverIO with all the $(.selector).waitForVisible() calls and now rely on cy.get(...) and c y.contains(...) commands with their default timeout. It will automatically keep on retrying to retrieve the DOM elements and if the test demanded a longer timeout, it is also configurable per command. With less worrying about the waiting logic, our tests became way more readable and easier to chain.
  • Vastly improved developer experience – Cypress provides a large toolkit with better and more extensive documentation for assertions, commands, and setup. We loved the options of using the Cypress GUI, running in headless mode, executing in the command-line, and chaining more intuitive Cypress commands.
  • Significantly better developer efficiency and debugging – When running the Cypress GUI, one has access to all of the browser console to see some helpful output, time travel debug and pause at certain commands in the command log to see before and after screenshots, inspect the DOM with the selector playground, and discern right away at which command the test failed. In WebdriverIO or STUI we struggled with observing the tests run over and over in a browser and then the console errors would not point us toward and would sometimes even lead us astray from where the test really failed in the code. When we opted to run the Cypress tests in headless mode, we got console errors, screenshots, and video recordings. With WebdriverIO we only had some screenshots and confusing console errors. These benefits resulted in us cranking out E2E tests much faster and with less overall time spent wondering why things went wrong. We recorded it took less developers and often around 2 to 3 times less days to write the same level of complicated tests with Cypress than with WebdriverIO or STUI.
  • Network stubbing and mocking – With WebdriverIO or STUI, there was no such thing as network stubbing or mocking in comparison to Cypress. Now we can have endpoints return certain values or we can wait for certain endpoints to finish through cy.server() and cy.route() .
  • Less time to set up locally – With WebdriverIO or STUI, there was a lot of time spent up front researching which reporters, test runners, assertions, and services to use, but with Cypress, it came bundled with everything and started working after just doing an npm install cypress.
  • Less time to set up with Docker – There are a bunch of ways to set up WebdriverIO with Selenium, browser, and application images that took us considerably more time and frustration to figure out in comparison to Cypress's Docker images to use right out of the gate.
  • Parallelization with various CICD providers – We were able to configure our Buildkite pipelines to spin up a certain number of AWS machines to run our Cypress tests in parallel to dramatically speed up the overall test run time and uncover any flakiness in tests using the same resources. The Dashboard Service would also recommend to us the optimal number of machines to spin up in parallel for the best test run times.
  • Paid Dashboard Service – When we run our Cypress tests in a Docker container in a Buildkite pipeline during CICD, our tests are recorded and stored for us to look at within the past month through a paid Dashboard Service. We have a parent organization for billing and separate projects for each frontend application to check out console output, screenshots, and recordings of all of our test runs.
  • Tests are way more consistent and maintainable – Tests passed way more consistently with Cypress in comparison to WebdriverIO and STUI where the tests kept on failing so much to the point where they were often ignored. Cypress tests failing more often signaled actual issues and bugs to look into or suggested better ways to refactor our tests to be less flaky. With WebdriverIO and STUI, we wasted a lot more time in maintaining those tests to be somewhat useful, whereas with Cypress, we would every now and then adjust the tests in response to changes in the backend services or minor changes in the UI.
  • Tests are faster – Builds passed way more consistently and overall test run times would be around 2 to 3 times faster when run serially without parallelization. We used to have overall test runs that would take hours with STUI and around 40 minutes with WebdriverIO, but now with way more tests and with the help of parallelization across many machine nodes, we can run over 200 tests in under 5 minutes .
  • Room to grow with added features in the future – With a steady open-source presence and dedicated Cypress team working towards releasing way more features and improvements to the Cypress infrastructure, we viewed Cypress as a safer bet to invest in rather than STUI, which would require us to engineer and solve a lot of the headaches ourselves, and WebdriverIO, which appeared to feel more stagnant in new features added but with the same baggage as other Selenium wrappers.

سلبيات

  • Lack of cross-browser support – As of this writing, we can only run our tests against Chrome. With WebdriverIO, we could run tests against Chrome, Firefox, Safari, and Opera. STUI also provided some cross-browser testing, though in a much limited form since we created a custom in-house solution
  • Cannot integrate with some third-party services – With WebdriverIO, we had the option to integrate with services like BrowserStack and Sauce Labs for cross-browser and device testing. However, with Cypress there are no such third-party integrations but there are some plugins with services like Applitools for visual regression testing available. STUI, on the other hand, also had some small integrations with TestRail , but as a compromise, we log out the TestRail links in our Cypress tests so we can refer back to them if we needed to.
  • Requires workarounds to test with iframes – There are some issues around handling iframes with Cypress. We ended up creating a global Cypress command to wrap how to deal with retrieving an iframe's contents as there is no specific API to deal with iframes like how WebdriverIO does.

To summarize our STUI, WebdriverIO, and Cypress comparison, we analyzed the overall developer experience (related to tools, writing tests, debugging, API, documentation, etc.), test run times, test passing rates, and maintenance as displayed in this table:

Following our analysis of the pros and cons of Cypress versus our previous solutions, it was pretty clear Cypress would be our best bet to accomplish our goal of writing fast, valuable, maintainable, and debuggable E2E tests we could integrate with CICD.

Though Cypress lacked features such as cross-browser testing and other integrations with third-party services that we could have had with STUI or WebdriverIO, we most importantly need tests that work more often than not and with the right tools to confidently fix broken ones. If we ever needed cross-browser testing or other integrations we could always still circle back and use our knowledge from our trials and experiences with WebdriverIO and STUI to still run a subset of tests with those frameworks.

We finally presented our findings to the rest of the frontend organization, engineering management, architects, and product. Upon demoing the Cypress test tools and showcasing our results between WebdriverIO/STUI and Cypress, we eventually received approval to standardize and adopt Cypress as our E2E testing library of choice for our frontend teams.

Step 7: Scaling to Other Frontend Teams

After successfully proving that using Cypress was the way to go for our use cases, we then focused on scaling it across all of our frontend teams' repos. We shared lessons learned and patterns of how to get up and running, how to write consistent, maintainable Cypress tests, and of how to hook those tests up during CICD or in scheduled Cypress Buildkite pipelines.

To promote greater visibility of test runs and gain access to a private monthly history of recordings, we established our own organization to be under one billing method to pay for the Dashboard Service with a certain recorded test run limit and maximum number of users in the organization to suit our needs.

Once we set up an umbrella organization, we invited developers and QAs from different frontend teams and each team would install Cypress, open up the Cypress GUI, and inspect the “Runs” and “Settings” tab to get the “Project ID” to place in their `cypress.json` configuration and “Record Key” to provide in their command options to start recording tests to the Dashboard Service. Finally, upon successfully setting up the project and recording tests to the Dashboard Service for the first time, logging into the Dashboard Service would show that team's repo under the “Projects” tab like this:

When we clicked a project like “mako”, we then had access to all of the test runs for that team's repo with quick access to console output, screenshots, and video recordings per test run upon clicking each row as shown below:

For more insights into our integration, we set up many separate dedicated test pipelines to run specific, crucial page tests on a schedule like say every couple hours to once per day. We also added functionality in our main Buildkite CICD deploy pipeline to select and trigger some tests against our feature branch environment and staging environment.

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

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

ما نتطلع إليه مع Cypress

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

  • دعم عبر المتصفحات - يعد هذا دعمًا كبيرًا لأن الكثير من الضغط الذي نواجهه من خلال تبني Cypress جاء من استخدام Chrome فقط مقارنة بالحلول القائمة على السيلينيوم ، والتي تدعم المتصفحات مثل Firefox و Chrome و Safari. لحسن الحظ ، فازت مؤسستنا باختبارات أكثر موثوقية وقابلة للصيانة والتصحيح ونأمل في تعزيز مجموعات الاختبار الخاصة بنا بمزيد من الاختبارات عبر المتصفح في المستقبل عندما يصدر فريق Cypress مثل هذا الدعم عبر المتصفحات.
  • إعادة كتابة طبقة الشبكة - هذا أيضًا ضخم لأننا نميل إلى استخدام Fetch API بكثافة في مناطق React الأحدث وفي مناطق تطبيق Backbone / Marionette القديمة ، ما زلنا نستخدم jQuery AJAX والمكالمات العادية المستندة إلى XHR. يمكننا بسهولة إيقاف الطلبات أو الاستماع إليها في مناطق XHR ، ولكن كان علينا القيام ببعض الحلول المبتكرة باستخدام إحضار polyfills لتحقيق نفس التأثير. من المفترض أن تساعد إعادة كتابة طبقة الشبكة في تخفيف هذه الآلام.
  • تحسينات متزايدة على خدمة لوحة المعلومات - لقد رأينا بالفعل بعض التغييرات الجديدة في واجهة المستخدم لخدمة لوحة المعلومات مؤخرًا ونأمل أن نواصل رؤيتها تنمو مع المزيد من تصورات الإحصائيات وتفاصيل البيانات المفيدة. نستخدم أيضًا ميزة الموازاة بشكل كبير ونتحقق من تسجيلات الاختبار الفاشلة في خدمة لوحة المعلومات في كثير من الأحيان ، لذلك سيكون من الجيد رؤية أي تحسينات تكرارية للتخطيط و / أو الميزات.

تبني السرو في المستقبل

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

بشكل عام ، تفوقت الوثائق وواجهة برمجة التطبيقات والأدوات الموجودة لدينا على أي سلبيات. بعد تجربة Cypress GUI وخدمة لوحة القيادة المدفوعة ، لم نرغب بالتأكيد في العودة إلى WebdriverIO أو حل Ruby Selenium المخصص.

لقد قمنا بتوصيل اختباراتنا مع Buildkite وحققنا هدفنا المتمثل في توفير طريقة لكتابة اختبارات أتمتة E2E متسقة وقابلة للتصحيح وقابلة للصيانة وقيمة لتطبيقات الواجهة الأمامية للتكامل مع CICD . لقد أثبتنا بالأدلة لبقية فرق الواجهة الأمامية ، والمسؤولين عن الهندسة العليا ، ومالكي المنتجات فوائد تبني Cypress وإسقاط WebdriverIO و STUI.

مئات الاختبارات في وقت لاحق عبر فرق الواجهة الأمامية في Twilio SendGrid واكتشفنا العديد من الأخطاء في بيئتنا المرحلية وتمكنا بسرعة من إصلاح أي اختبارات غير مستقرة من جانبنا بثقة أكبر بكثير من أي وقت مضى. لم يعد المطورون و QAs يخشون فكرة كتابة اختبارات E2E ولكنهم الآن يتطلعون إلى كتابتها لكل ميزة جديدة نصدرها أو لكل ميزة قديمة يمكن أن تستخدم المزيد من التغطية.