اختبار الانحدار البصري باستخدام السرو: نهج عملي

نشرت: 2022-03-11

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

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

اختبار الانحدار البصري ليس مفهوماً جديداً ؛ الكثير من المشاريع الأخرى في Toptal تستخدمه بالفعل ، بما في ذلك بيكاسو نفسه.

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

اختبار الانحدار البصري من خلال اختبار واجهة المستخدم

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

من السرو إلى لقطات

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

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

لقطة شاشة تصور النتيجة المتوقعة والنتيجة الفعلية للتشغيل التجريبي.
الشكل 1. مثال على اختلافات طفيفة في البكسل تؤدي إلى صور سلبية خاطئة

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

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

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

تسخير واجهات برمجة التطبيقات لمقارنة الصور

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

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

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

لجأنا إلى واجهة برمجة تطبيقات Cypress لإنشاء الصور. يمكن cy.screenshot() ، خارج الصندوق ، إنشاء صور فردية للمكونات ، وتسمح لنا واجهة برمجة تطبيقات After Screenshot بإعادة تسمية الملفات وتغيير الأدلة وتمييز عمليات الانحدار المرئي عن تلك القياسية. من خلال الجمع بين الاثنين ، أنشأنا عمليات تشغيل لم تؤثر على اختباراتنا الوظيفية ومكنتنا من تخزين الصور في مجلداتها المناسبة.

أولاً ، قمنا بتوسيع ملف index.js في دليل المكونات الإضافية لدينا لدعم نوعي التشغيل الجديدين (خط الأساس والمقارنة). بعد ذلك ، نضع مسار صورنا وفقًا لنوع التشغيل:

 // plugins/index.js const fs = require('fs') const path = require('path') module.exports = (on, config) => { // Adding these values to your config object allows you to access them in your tests. config.env.baseline = process.env.BASELINE || false config.env.comparison = process.env.COMPARISON || false on('after:screenshot', details => { // We only want to modify the behavior of baseline and comparison runs. if (config.env.baseline || config.env.comparison) { // We keep track of the file name and number to make sure they are saved in the proper order and in their relevant folders. // An alternative would have been to look up the folder for the latest image, but this was the simpler approach. let lastScreenshotFile = '' let lastScreenshotNumber = 0 // We append the proper suffix number to the image, create the folder, and move the file. const createDirAndRename = filePath => { if (lastScreenshotFile === filePath) { lastScreenshotNumber++ } else { lastScreenshotNumber = 0 } lastScreenshotFile = filePath const newPath = filePath.replace( '.png', ` #${lastScreenshotNumber}.png` ) return new Promise((resolve, reject) => { fs.mkdir(path.dirname(newPath), { recursive: true }, mkdirErr => { if (mkdirErr) { return reject(mkdirErr) } fs.rename(details.path, newPath, renameErr => { if (renameErr) { return reject(renameErr) } resolve({ path: newPath }) }) }) }) } const screenshotPath = `visualComparison/${config.env.baseline ? 'baseline' : 'comparison'}` return createDirAndRename(details.path .replace('cypress/integration', screenshotPath) .replace('All Specs', screenshotPath) ) } }) return config }

ثم استدعينا كل من عمليات التشغيل عن طريق إضافة متغير البيئة المقابل لاستدعاء Cypress في حزمة المشروع. package.json :

 "scripts": { "cypress:baseline": "BASELINE=true yarn cypress:open", "cypress:comparison": "COMPARISON=true yarn cypress:open" }

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

لقطة شاشة تعرض الصور التي تم التقاطها أثناء التشغيل ونقلها إلى المجلدات.
الشكل 3. نتائج التشغيل المرئية

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

 Cypress.Commands.add("getAndScreenshot", (selector, options) => { // Note: You might need to tweak the command when getting multiple elements. return cy.get(selector).screenshot() }); it("get overwrite", () => { cy.visit("https://example.cypress.io/commands/actions"); cy.getAndScreenshot(".action-email") })

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

النتائج التي تم الحصول عليها عن طريق اختبار الانحدار البصري

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

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

قبل وبعد مثال على تخطيط مكون معطل قليلاً ، عرض نص غير محاذي بجوار مربع اختيار في الصورة "بعد".
الشكل 5. مثال على تخطيط مكون مكسور قليلاً

إضافة قيمة إلى اختبار واجهة المستخدم

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

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

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