การทดสอบการถดถอยด้วยภาพด้วย Cypress: แนวทางปฏิบัติ
เผยแพร่แล้ว: 2022-03-11ทุกครั้งที่มีการเปิดตัว Picasso ไลบรารีองค์ประกอบเวอร์ชันใหม่ เราจะอัปเดตแอปพลิเคชันส่วนหน้าทั้งหมดของเราเพื่อให้ได้รับประโยชน์สูงสุดจากคุณลักษณะใหม่ และปรับการออกแบบของเราให้สอดคล้องในทุกส่วนของไซต์
เมื่อเดือนที่แล้ว เราได้เปิดตัว Picasso อัปเดตใน Toptal Talent Portal ซึ่งเป็นแพลตฟอร์มที่คนเก่งของเราใช้ในการหางานและโต้ตอบกับลูกค้า การรู้ว่าการเปิดตัวจะมาพร้อมกับการเปลี่ยนแปลงการออกแบบครั้งสำคัญ และในความพยายามที่จะลดปัญหาที่ไม่คาดคิด ควรใช้เทคนิคการทดสอบการถดถอยด้วยภาพเพื่อช่วยเราค้นหาปัญหาก่อนการเปิดตัว
การทดสอบการถดถอยด้วยภาพไม่ใช่แนวคิดใหม่ โปรเจ็กต์อื่นๆ มากมายที่ Toptal ใช้อยู่แล้ว รวมถึง Picasso ด้วย
เครื่องมือต่างๆ เช่น Percy, Happo และ Chromatic สามารถใช้เพื่อช่วยให้ทีมสร้างไปป์ไลน์การถดถอยภาพที่ดี และเราได้พิจารณาเพิ่มเครื่องมือเหล่านี้ในตอนแรก ในที่สุดเราก็ตัดสินใจว่าขั้นตอนการตั้งค่าจะใช้เวลานานเกินไปและอาจทำให้กำหนดการของเราเสียหายได้ เรามีกำหนดวันที่สำหรับการหยุดโค้ดเพื่อเริ่มการย้ายข้อมูลแล้ว และเหลือเวลาอีกไม่กี่วันก่อนถึงกำหนดส่ง เราไม่มีทางเลือกอื่นนอกจากต้องสร้างสรรค์
การทดสอบการถดถอยของภาพผ่านการทดสอบ UI
แม้ว่าเราจะไม่มีการทดสอบการถดถอยด้วยภาพในโครงการ แต่เรามีความครอบคลุมที่ดีของการทดสอบการรวม UI โดยใช้ Cypress แม้ว่าจะไม่ใช่เครื่องมือที่ใช้เป็นหลัก แต่ Cypress มีหน้าเดียวในเอกสารประกอบสำหรับการทดสอบด้วยภาพโดยเฉพาะ และหน้าอื่นที่แสดงรายการปลั๊กอินทั้งหมดที่มีอยู่เพื่อช่วยกำหนดค่า Cypress สำหรับการทดสอบด้วยภาพ
จาก Cypress สู่สกรีนช็อต
หลังจากอ่านเอกสารที่มีอยู่แล้ว เราตัดสินใจลองใช้ cypress-snapshot-plugin ใช้เวลาเพียงไม่กี่นาทีในการตั้งค่า และเมื่อเราทำสำเร็จ เราก็รู้ได้อย่างรวดเร็วว่าเราไม่ได้แสวงหาผลลัพธ์ของการถดถอยภาพแบบดั้งเดิม
เครื่องมือการถดถอยภาพส่วนใหญ่ช่วยระบุการเปลี่ยนแปลงที่ไม่ต้องการโดยการเปรียบเทียบสแนปชอตและการตรวจจับความแตกต่างของพิกเซลระหว่างเส้นฐานที่รู้จักและยอมรับกับเวอร์ชันที่แก้ไขของหน้าหรือส่วนประกอบ หากความแตกต่างของพิกเซลมากกว่าเกณฑ์ความคลาดเคลื่อนที่กำหนด หน้าหรือส่วนประกอบจะถูกตั้งค่าสถานะให้ตรวจสอบด้วยตนเอง อย่างไรก็ตาม ในรุ่นนี้ เรารู้ว่าเราจะมีการเปลี่ยนแปลงเล็กๆ น้อยๆ หลายอย่างในองค์ประกอบ UI ส่วนใหญ่ของเรา ดังนั้นการตั้งค่าเกณฑ์จึงไม่มีผล แม้ว่าองค์ประกอบที่กำหนดจะแตกต่างกัน 100% แต่องค์ประกอบนั้นอาจยังถูกต้องในบริบทของเวอร์ชันใหม่ ในทำนองเดียวกัน ส่วนเบี่ยงเบนที่มีขนาดเล็กเพียงไม่กี่พิกเซลอาจหมายความว่าส่วนประกอบไม่เหมาะสำหรับการผลิตในปัจจุบัน
เมื่อถึงจุดนั้น สองสิ่งที่ตัดกันก็ชัดเจน: การสังเกตความแตกต่างของพิกเซลไม่ได้ช่วยระบุปัญหา และการเปรียบเทียบองค์ประกอบแบบเคียงข้างกันเป็นสิ่งที่เราต้องการอย่างแม่นยำ เราวางปลั๊กอินสแน็ปช็อตไว้ข้างๆ และเริ่มต้นสร้างคอลเลกชันรูปภาพด้วยส่วนประกอบของเราก่อนและหลังการอัปเดต Picasso ด้วยวิธีนี้ เราจึงสามารถสแกนการเปลี่ยนแปลงทั้งหมดได้อย่างรวดเร็วเพื่อดูว่าเวอร์ชันใหม่ยังคงตรงกับความต้องการของไซต์และมาตรฐานของไลบรารีหรือไม่
แผนใหม่คือการจับภาพหน้าจอของส่วนประกอบ เก็บไว้ในเครื่อง ถ่ายภาพหน้าจอใหม่ของส่วนประกอบเดียวกันในสาขาด้วยเวอร์ชัน Picasso ที่อัปเดต แล้วรวมเข้าด้วยกันเป็นภาพเดียว ในท้ายที่สุด วิธีการใหม่นี้ไม่ได้แตกต่างไปจากที่เราเริ่มต้นมากนัก แต่มันทำให้เรามีความยืดหยุ่นมากขึ้นในระหว่างขั้นตอนการใช้งาน เนื่องจากเราไม่จำเป็นต้องนำเข้าปลั๊กอินและใช้คำสั่งใหม่อีกต่อไป
การควบคุม API สำหรับรูปภาพเปรียบเทียบ
ด้วยเป้าหมายที่ชัดเจน ถึงเวลาแล้วที่จะดูว่า Cypress สามารถช่วยเราได้ภาพหน้าจอที่เราต้องการได้อย่างไร ดังที่กล่าวไว้ เรามีการทดสอบ UI จำนวนมากซึ่งครอบคลุม Talent Portal ส่วนใหญ่ ดังนั้นในความพยายามที่จะรวบรวมส่วนประกอบที่สำคัญให้ได้มากที่สุด เราจึงตัดสินใจถ่ายภาพหน้าจอขององค์ประกอบแต่ละรายการหลังจากการโต้ตอบแต่ละครั้ง
อีกวิธีหนึ่งคือการจับภาพหน้าจอของทั้งหน้าในช่วงเวลาสำคัญระหว่างการทดสอบ แต่เราตัดสินใจว่าภาพเหล่านั้นจะเปรียบเทียบได้ยากเกินไป นอกจากนี้ การเปรียบเทียบดังกล่าวอาจมีแนวโน้มที่จะเกิดข้อผิดพลาดจากมนุษย์มากขึ้น เช่น ไม่มีการเปลี่ยนแปลงส่วนท้าย
ทางเลือกที่สามคือต้องผ่านทุกกรณีทดสอบเดียวเพื่อตัดสินใจว่าจะจับภาพอะไร แต่นั่นจะต้องใช้เวลามากกว่านี้มาก ดังนั้นการยึดติดกับองค์ประกอบทั้งหมดที่ใช้ในหน้าจึงดูเหมือนเป็นการประนีประนอมในทางปฏิบัติ
เราหันไปใช้ API ของ Cypress เพื่อสร้างภาพ คำสั่ง cy.screenshot() สามารถสร้างภาพส่วนประกอบแต่ละภาพได้ทันที และ After Screenshot API ช่วยให้เราสามารถเปลี่ยนชื่อไฟล์ เปลี่ยนไดเร็กทอรี และแยกแยะการถดถอยของภาพออกจากไฟล์มาตรฐานได้ เมื่อรวมทั้งสองสิ่งนี้เข้าด้วยกัน เราได้สร้างการรันที่ไม่ส่งผลต่อการทดสอบการทำงานของเรา และทำให้เราสามารถจัดเก็บภาพในโฟลเดอร์ที่เหมาะสมได้
อันดับแรก เราขยายไฟล์ 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" }เมื่อเรารันคำสั่งใหม่แล้ว เราจะเห็นว่าภาพหน้าจอทั้งหมดที่ถ่ายระหว่างการรันถูกย้ายไปยังโฟลเดอร์ที่เหมาะสม
ต่อไป เราพยายามที่จะเขียนทับ 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 ภาพ! ความแตกต่างบางอย่างมองเห็นได้ง่ายโดยการเปิด Talent Portal แต่ปัญหาบางอย่างก็ไม่ชัดเจนนัก
การเพิ่มมูลค่าให้กับการทดสอบ UI
อย่างแรกเลย การทดสอบการถดถอยด้วยภาพเพิ่มเติมพิสูจน์แล้วว่ามีประโยชน์ และค้นพบปัญหาสองสามประการที่เราอาจมองข้ามไปหากไม่มีการทดสอบเหล่านี้ แม้ว่าเราจะคาดหวังความแตกต่างในองค์ประกอบของเรา แต่การรู้ว่าอะไรเปลี่ยนแปลงจริง ๆ ได้ช่วยให้กรณีที่เป็นปัญหาแคบลง ดังนั้น หากโปรเจ็กต์ของคุณมีอินเทอร์เฟซ แต่คุณยังไม่ได้ทำการทดสอบเหล่านี้ ให้ไปที่มัน!
บทเรียนที่สองที่นี่ และที่สำคัญกว่านั้นคือ เราได้รับการเตือนอีกครั้งว่าความสมบูรณ์แบบเป็นศัตรูของความดี หากเราตัดความเป็นไปได้ของการทดสอบการถดถอยด้วยภาพสำหรับรุ่นนี้เนื่องจากไม่มีการตั้งค่าล่วงหน้า เราอาจพลาดข้อบกพร่องบางประการในระหว่างการย้ายข้อมูล แต่เราตกลงกันในแผนซึ่งถึงแม้จะไม่สมบูรณ์แบบ แต่ดำเนินการได้เร็ว เราดำเนินการตามแผน และมันก็ได้ผล
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการนำไปป์ไลน์การถดถอยภาพที่มีประสิทธิภาพไปใช้ในโครงการของคุณ โปรดดูหน้าการทดสอบด้วยภาพของ Cypress เลือกเครื่องมือที่เหมาะกับความต้องการของคุณมากที่สุด และชมวิดีโอแนะนำการใช้งาน
