แนวคิดสำหรับการกำหนดค่า จัดระเบียบ และรวมการทดสอบ Cypress ของคุณ #frontend@twiiosendgrid

เผยแพร่แล้ว: 2020-12-15

เราได้เขียนการทดสอบ Cypress มากมายในเว็บแอปพลิเคชันและทีมส่วนหน้าต่างๆ ที่ Twilio SendGrid เมื่อการทดสอบของเราปรับขยายเป็นคุณลักษณะและหน้าต่างๆ เราพบตัวเลือกการกำหนดค่าที่มีประโยชน์บางอย่าง และพัฒนาวิธีที่จะรักษาจำนวนไฟล์ที่เพิ่มขึ้น ตัวเลือกองค์ประกอบของหน้า และค่าการหมดเวลาไปพร้อมกันได้ดียิ่งขึ้น

เรามุ่งมั่นที่จะแสดงเคล็ดลับและแนวคิดเหล่านี้แก่คุณในการกำหนดค่า จัดระเบียบ และรวบรวมสิ่งที่เกี่ยวข้องกับ Cypress ของคุณเอง ดังนั้นอย่าลังเลที่จะดำเนินการกับพวกเขาสิ่งที่ดีที่สุดสำหรับคุณและทีมของคุณ

โพสต์ในบล็อกนี้ถือว่าคุณมีความรู้เกี่ยวกับการทดสอบ Cypress และกำลังมองหาแนวคิดเกี่ยวกับการบำรุงรักษาและปรับปรุงการทดสอบ Cypress ของคุณให้ดีขึ้น อย่างไรก็ตาม หากคุณอยากทราบข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชัน การยืนยัน และรูปแบบทั่วไป คุณอาจพบว่ามีประโยชน์ในการเขียนการทดสอบ Cypress กับสภาพแวดล้อมที่แยกจากกัน คุณสามารถดูบล็อกโพสต์ภาพรวมหนึ่งพันฟุตนี้แทน

ไม่เช่นนั้น มาดำเนินการต่อและแนะนำเคล็ดลับการกำหนดค่าบางอย่างสำหรับ Cypress ก่อน

การกำหนดค่า cypress.json . ของคุณ

ไฟล์ cypress.json เป็นที่ที่คุณสามารถตั้งค่าคอนฟิกทั้งหมดสำหรับการทดสอบ Cypress ของคุณ เช่น ระยะหมดเวลาพื้นฐานสำหรับคำสั่ง Cypress ของคุณ ตัวแปรสภาพแวดล้อม และคุณสมบัติอื่นๆ

นี่คือเคล็ดลับบางประการสำหรับคุณที่จะลองใช้ในการกำหนดค่าของคุณ:

  • ปรับแต่งการหมดเวลาคำสั่งพื้นฐานของคุณ ดังนั้นคุณไม่จำเป็นต้องเพิ่ม { timeout: timeoutInMs } ตลอดคำสั่ง Cypress ของคุณเสมอไป คนจรจัดกับตัวเลขจนกว่าคุณจะพบความสมดุลที่เหมาะสมสำหรับการตั้งค่าเช่น "defaultCommandTimeout" "requestTimeout" และ "responseTimeout"
  • หากการทดสอบของคุณเกี่ยวข้องกับ iframes คุณมักจะต้องตั้งค่า “chromeWebSecurity” เป็นเท็จ เพื่อให้คุณสามารถเข้าถึง iframes ข้ามต้นทางในแอปพลิเคชันของคุณได้
  • ลองบล็อกการตลาด การวิเคราะห์ และสคริปต์การบันทึกของบุคคลที่สามเมื่อคุณดำเนินการทดสอบ Cypress เพื่อเพิ่มความเร็วและไม่ทริกเกอร์เหตุการณ์ที่ไม่ต้องการ คุณสามารถตั้งค่ารายการปฏิเสธได้อย่างง่ายดายด้วยคุณสมบัติ "blacklistHosts" (หรือคุณสมบัติ "blockHosts" ใน Cypress 5.0.0) โดยรับอาร์เรย์ของสตริง globs ที่ตรงกับเส้นทางโฮสต์ของบุคคลที่สาม
  • ปรับขนาดวิวพอร์ตเริ่มต้นด้วย "viewportWidth" และ "viewportHeight" เมื่อคุณเปิด Cypress GUI เพื่อให้มองเห็นได้ง่ายขึ้น
  • หากมีข้อกังวลด้านหน่วยความจำใน Docker สำหรับการทดสอบที่หนักหน่วง หรือคุณต้องการช่วยให้สิ่งต่างๆ มีประสิทธิภาพมากขึ้น คุณสามารถลองแก้ไข “numTestsKeptInMemory” เป็นจำนวนที่น้อยกว่าค่าเริ่มต้นและตั้งค่า “videoUploadOnPasses” เป็น false เพื่อเน้นการอัปโหลดวิดีโอสำหรับการทดสอบที่ล้มเหลว วิ่งเท่านั้น

ในบันทึกอื่น หลังจากปรับแต่งการกำหนดค่า Cypress ของคุณแล้ว คุณยังสามารถเพิ่ม TypeScript และพิมพ์การทดสอบ Cypress ของคุณได้ดีขึ้น เช่นเดียวกับที่เราทำในบล็อกโพสต์นี้ สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับการเติมข้อความอัตโนมัติ การเตือนประเภท หรือข้อผิดพลาดเมื่อเรียกใช้และเชื่อมโยงฟังก์ชัน (เช่น cy.task('someTaskPluginFunction) , Cypress.env('someEnvVariable') หรือ cy.customCommand() )

คุณยังอาจต้องการทดสอบด้วยการตั้งค่าไฟล์ cypress.json พื้นฐานและโหลดไฟล์การกำหนดค่า Cypress แยกต่างหากสำหรับแต่ละสภาพแวดล้อมการทดสอบ เช่น staging.json เมื่อคุณเรียกใช้สคริปต์ Cypress ที่แตกต่างกันใน package.json ของคุณ เอกสาร Cypress ช่วยให้คุณเข้าใจแนวทางนี้ได้อย่างดี

การจัดระเบียบโฟลเดอร์ Cypress ของคุณ

Cypress ได้ตั้งค่าโฟลเดอร์ cypress ระดับบนสุดพร้อมโครงสร้างแบบไกด์แล้ว เมื่อคุณเริ่มต้น Cypress ใน codebase ของคุณเป็นครั้งแรก ซึ่งรวมถึงโฟลเดอร์อื่นๆ เช่น integration สำหรับไฟล์ข้อมูลจำเพาะ ไฟล์ fixtures สำหรับไฟล์ข้อมูล JSON plugins สำหรับ cy.task(...) และการกำหนดค่าอื่นๆ และโฟลเดอร์ support สำหรับคำสั่งและประเภทที่กำหนดเอง

หลักการง่ายๆ ที่ควรปฏิบัติตามเมื่อจัดระเบียบภายในโฟลเดอร์ Cypress ส่วนประกอบ React หรือโค้ดใดๆ โดยทั่วไปคือการจัดวางสิ่งของต่างๆ เข้าด้วยกันที่มีแนวโน้มว่าจะเปลี่ยนแปลงร่วมกัน ในกรณีนี้ เนื่องจากเรากำลังจัดการกับเว็บแอปพลิเคชันในเบราว์เซอร์ แนวทางหนึ่งที่ปรับขนาดได้ดีคือการจัดกลุ่มสิ่งต่างๆ ในโฟลเดอร์ตามหน้าหรือคุณลักษณะที่ครอบคลุม

เราได้สร้างโฟลเดอร์ pages แยกกันโดยแบ่งพาร์ติชั่นตามชื่อฟีเจอร์ เช่น “SenderAuthentication” เพื่อเก็บออบเจ็กต์เพจทั้งหมดภายใต้เส้นทาง /settings/sender_auth เช่น “DomainAuthentication” (/settings/sender_auth/domains/**/*) และ “LinkBranding” (/settings/sender_auth/links/**/*) ในโฟลเดอร์ plugins ของเรา เรายังทำสิ่งเดียวกันกับการจัดระเบียบไฟล์ปลั๊กอิน cy.task(...) ทั้งหมดสำหรับคุณลักษณะหรือหน้าบางอย่างในโฟลเดอร์เดียวกันสำหรับการตรวจสอบผู้ส่ง และเราปฏิบัติตามแนวทางเดียวกันสำหรับโฟลเดอร์ integration ของเรา ไฟล์ข้อมูลจำเพาะ เราได้ปรับขนาดการทดสอบเป็นไฟล์ข้อมูลจำเพาะ ออบเจ็กต์ของเพจ ปลั๊กอิน และไฟล์อื่นๆ หลายร้อยรายการในหนึ่งในฐานโค้ดของเรา และง่ายต่อการนำทางผ่าน

นี่คือโครงร่างของวิธีที่เราจัดระเบียบโฟลเดอร์ cypress

อีกสิ่งหนึ่งที่ควรพิจารณาเมื่อจัดระเบียบโฟลเดอร์ integration ของคุณ (ซึ่งมีข้อกำหนดทั้งหมดของคุณอยู่) อาจทำให้แยกไฟล์ข้อมูลจำเพาะตามลำดับความสำคัญของการทดสอบ ตัวอย่างเช่น คุณอาจมีการทดสอบลำดับความสำคัญและค่าสูงสุดทั้งหมดในโฟลเดอร์ "P1" และการทดสอบที่มีลำดับความสำคัญที่สองในโฟลเดอร์ "P2" เพื่อให้คุณสามารถเรียกใช้การทดสอบ "P1" ทั้งหมดโดยการตั้งค่าตัวเลือก --spec เช่น --spec 'cypress/integration/P1/**/*' .

สร้างลำดับชั้นของโฟลเดอร์ข้อมูลจำเพาะที่เหมาะกับคุณ เพื่อให้คุณสามารถจัดกลุ่มข้อกำหนดร่วมกันได้อย่างง่ายดาย ไม่เพียงแต่ตามหน้าหรือคุณสมบัติ เช่น --spec 'cypress/integration/SomePage/**/*' แต่ยังรวมถึงเกณฑ์อื่นๆ เช่น ลำดับความสำคัญ ผลิตภัณฑ์ หรือสิ่งแวดล้อม

การรวมตัวเลือกองค์ประกอบ

เมื่อพัฒนาส่วนประกอบ React ของเพจ โดยปกติเราจะเพิ่มระดับของการทดสอบหน่วยและการรวมเข้ากับ Jest และ Enzyme ในช่วงท้ายของการพัฒนาคุณลักษณะ เราได้เพิ่มการทดสอบ Cypress E2E อีกชั้นหนึ่งเพื่อให้แน่ใจว่าทุกอย่างใช้งานได้กับแบ็กเอนด์ ทั้งการทดสอบหน่วยสำหรับส่วนประกอบ React และออบเจ็กต์หน้าสำหรับการทดสอบ Cypress E2E ของเราจำเป็นต้องมีตัวเลือกสำหรับส่วนประกอบ/องค์ประกอบ DOM บนหน้าที่เราต้องการโต้ตอบและยืนยัน

เมื่อเราอัปเดตหน้าและส่วนประกอบเหล่านั้น มีโอกาสเกิดการเบี่ยงเบนและข้อผิดพลาดจากการต้องซิงโครไนซ์หลายตำแหน่งตั้งแต่ตัวเลือกการทดสอบหน่วยไปจนถึงออบเจ็กต์หน้า Cypress ไปจนถึงรหัสส่วนประกอบจริง หากเราอาศัยเพียงชื่อคลาสที่เกี่ยวข้องกับสไตล์ คงเป็นเรื่องยากที่จะลืมอัปเดตสถานที่ทั้งหมดที่อาจพัง แต่เราเพิ่ม "data-hook", "data-testid" หรือแอตทริบิวต์อื่นๆ ที่มีชื่อสม่ำเสมอว่า "data-*" ให้กับส่วนประกอบและองค์ประกอบเฉพาะบนหน้าที่เราต้องการจดจำและเขียนตัวเลือกในเลเยอร์การทดสอบของเรา

เราสามารถผนวกแอตทริบิวต์ “data-hook” เข้ากับองค์ประกอบหลายๆ อย่างของเราได้ แต่เรายังคงต้องการวิธีจัดกลุ่มองค์ประกอบทั้งหมดไว้ในที่เดียวเพื่ออัปเดตและนำกลับมาใช้ใหม่ในไฟล์อื่นๆ เราหาวิธีพิมพ์เพื่อจัดการตัวเลือก "data-hook" ทั้งหมดเหล่านี้เพื่อกระจายไปยังส่วนประกอบ React ของเราและเพื่อนำไปใช้ในการทดสอบหน่วยและตัวเลือกออบเจ็กต์หน้าเพื่อให้ใช้ซ้ำและบำรุงรักษาได้ง่ายขึ้นในออบเจกต์ที่ส่งออก

สำหรับโฟลเดอร์ระดับบนสุดของแต่ละหน้า เราจะสร้างไฟล์ hooks.ts ที่จัดการและส่งออกวัตถุด้วยชื่อคีย์ที่อ่านได้สำหรับองค์ประกอบและตัวเลือก CSS สตริงจริงสำหรับองค์ประกอบนั้นเป็นค่า เราจะเรียกสิ่งเหล่านี้ว่า “ตัวเลือกการอ่าน” เนื่องจากเราจำเป็นต้องอ่านและใช้รูปแบบตัวเลือก CSS สำหรับองค์ประกอบในการเรียกเช่น wrapper.find(“[data-hook='selector']”) ในการทดสอบหน่วยของเราหรือของ Cypress cy.get(“[data-hook='selector']”) ในวัตถุหน้าของเรา การเรียกเหล่านี้จะดูสะอาดตาเช่น wrapper.find(Selectors.someElement) หรือ cy.get(Selectors.someElement)

ข้อมูลโค้ดต่อไปนี้ครอบคลุมถึงสาเหตุและวิธีที่เราใช้ตัวเลือกการอ่านเหล่านี้ในทางปฏิบัติ

ในทำนองเดียวกัน เรายังส่งออกวัตถุด้วยชื่อคีย์ที่อ่านได้สำหรับองค์ประกอบและด้วยวัตถุเช่น { “data-hook”: “selector” } เป็นค่า เราจะเรียกสิ่งเหล่านี้ว่า "ตัวเลือกการเขียน" เนื่องจากเราจำเป็นต้องเขียนหรือกระจายวัตถุเหล่านี้ไปยังองค์ประกอบ React เป็นอุปกรณ์ประกอบฉากเพื่อเพิ่มแอตทริบิวต์ "data-hook" ให้กับองค์ประกอบพื้นฐานได้สำเร็จ เป้าหมายคือจะทำอะไรแบบนี้ และองค์ประกอบ DOM จริงที่อยู่ด้านล่าง—สมมติว่าอุปกรณ์ประกอบฉากถูกส่งต่อไปยังองค์ประกอบ JSX อย่างถูกต้อง—จะมีชุดแอตทริบิวต์ “data-hook=” ด้วย

ข้อมูลโค้ดต่อไปนี้ครอบคลุมถึงสาเหตุและวิธีที่เราใช้ตัวเลือกการเขียนเหล่านี้ในทางปฏิบัติ

เราสามารถสร้างออบเจ็กต์เพื่อรวมตัวเลือกการอ่านและเขียนตัวเลือกเพื่ออัปเดตสถานที่น้อยลง แต่ถ้าเราต้องเขียนตัวเลือกจำนวนมากสำหรับหน้าที่ซับซ้อนกว่าบางหน้าของเรา การสร้างตัวคุณเองอาจเสี่ยงต่อข้อผิดพลาดมากกว่า ดังนั้นเรามาสร้างฟังก์ชันเพื่อสร้างตัวเลือกการอ่านเหล่านี้และเขียนตัวเลือกเพื่อส่งออกสำหรับบางหน้าในที่สุด

สำหรับฟังก์ชันตัวสร้างตัวเลือกการอ่าน เราจะวนซ้ำคุณสมบัติของวัตถุอินพุตและสร้างสตริงตัวเลือก CSS [data-hook=”selector”] สำหรับแต่ละชื่อองค์ประกอบ หากค่าที่สอดคล้องกันของคีย์เป็น null เราจะถือว่าชื่อองค์ประกอบในวัตถุอินพุตจะเหมือนกับค่า "data-hook" เช่น { someElement: null } => { someElement: '[data-hook=”someElement”] } . มิฉะนั้น หากค่าที่สอดคล้องกันของคีย์ไม่เป็นค่าว่าง เราสามารถเลือกที่จะแทนที่ค่า “data-hook” เช่น { someElement: “newSelector” } => { someElement: '[data-hook=”newSelector”]' }

สำหรับฟังก์ชันตัวสร้างตัวเลือกการเขียน เราจะวนซ้ำคุณสมบัติของวัตถุอินพุตและสร้างอ็อบเจ็กต์ { “data-hook”: “selector” } สำหรับแต่ละชื่อองค์ประกอบ หากค่าที่สอดคล้องกันของคีย์เป็น null เราจะถือว่าชื่อองค์ประกอบในวัตถุอินพุตจะเหมือนกับค่า "data-hook" เช่น { someElement: null } => { someElement: { “data-hook”: “someElement” } } . มิฉะนั้น หากค่าที่สอดคล้องกันของคีย์ไม่เป็น null เราสามารถเลือกที่จะแทนที่ค่า “data-hook” เช่น { someElement: “newSelector” } => { someElement: { “data-hook”: “newSelector” }' } .

การใช้ฟังก์ชันตัวสร้างทั้งสองนี้ เราสร้างตัวเลือกการอ่านและเขียนออบเจ็กต์ตัวเลือกสำหรับหน้าและส่งออกเพื่อนำไปใช้ใหม่ในการทดสอบหน่วยและออบเจ็กต์หน้า Cypress โบนัสอีกประการหนึ่งคือวัตถุเหล่านี้ถูกพิมพ์ในลักษณะที่เราไม่สามารถทำ Selectors.unknownElement หรือ WriteSelectors.unknownElement ได้โดยบังเอิญในไฟล์ TypeScript ของเราเช่นกัน ก่อนส่งออกตัวเลือกการอ่าน เรายังอนุญาตให้เพิ่มองค์ประกอบพิเศษและการแมปตัวเลือก CSS สำหรับส่วนประกอบบุคคลที่สามที่เราไม่สามารถควบคุมได้ ในบางกรณี เราไม่สามารถเพิ่มแอตทริบิวต์ "data-hook" ให้กับองค์ประกอบบางอย่างได้ ดังนั้นเราจึงยังต้องเลือกโดยแอตทริบิวต์ รหัส และคลาสอื่นๆ และเพิ่มคุณสมบัติเพิ่มเติมให้กับอ็อบเจ็กต์ตัวเลือกการอ่านดังที่แสดงด้านล่าง

รูปแบบนี้ช่วยให้เราจัดระเบียบกับตัวเลือกทั้งหมดของเราสำหรับหน้าและเวลาที่เราต้องอัปเดตสิ่งต่างๆ เราขอแนะนำให้คุณตรวจสอบวิธีจัดการตัวเลือกทั้งหมดเหล่านี้ในออบเจกต์บางประเภทหรือด้วยวิธีอื่นๆ เพื่อลดจำนวนไฟล์ที่คุณต้องเผชิญเมื่อมีการเปลี่ยนแปลงในอนาคต

กำลังรวมระยะหมดเวลา

สิ่งหนึ่งที่เราสังเกตเห็นหลังจากเขียนการทดสอบ Cypress หลายครั้งก็คือตัวเลือกของเราสำหรับองค์ประกอบบางอย่างจะหมดเวลาและใช้เวลานานกว่าค่าการหมดเวลาเริ่มต้นที่ตั้งไว้ใน cypress.json เช่น "defaultCommandTimeout" และ "responseTimeout" เราจะย้อนกลับไปและปรับหน้าบางหน้าที่ต้องใช้ระยะหมดเวลานานขึ้น แต่เมื่อเวลาผ่านไป จำนวนของค่าการหมดเวลาตามอำเภอใจก็เพิ่มขึ้น และการรักษาไว้ก็กลายเป็นเรื่องยากขึ้นสำหรับการเปลี่ยนแปลงในวงกว้าง

ด้วยเหตุนี้ เราจึงรวมการหมดเวลาของเราไว้ในออบเจ็กต์ที่เริ่มต้นเหนือ "defaultCommandTimeout" ซึ่งอยู่ในช่วงห้าถึงสิบวินาทีเพื่อให้ครอบคลุมการหมดเวลาทั่วไปส่วนใหญ่สำหรับตัวเลือกของเรา เช่น cy.get(...) หรือ cy.contains(...) นอกเหนือจากการหมดเวลาเริ่มต้นนั้น เราจะขยายขนาดเป็น "สั้น" "ปานกลาง" "ยาว" "xlong" และ "xxlong" ภายในวัตถุหมดเวลา เราสามารถนำเข้าที่ใดก็ได้ในไฟล์ของเราเพื่อใช้ในคำสั่ง Cypress เช่น cy.get(“someElement”, { timeout: timeouts.short }) หรือ cy.task('pluginName', {}, { timeout: timeouts.xlong }) หลังจากแทนที่การหมดเวลาของเราด้วยค่าเหล่านี้จากวัตถุที่นำเข้าแล้ว เรามีที่เดียวในการอัปเดตเพื่อขยายหรือลดขนาดเวลาที่ใช้สำหรับการหมดเวลาบางอย่าง

ตัวอย่างของวิธีที่คุณสามารถเริ่มต้นด้วยสิ่งนี้ได้อย่างง่ายดายแสดงอยู่ด้านล่าง

ห่อ

เมื่อชุดทดสอบ Cypress ของคุณเติบโตขึ้น คุณอาจพบปัญหาเดียวกันกับที่เราทำเมื่อต้องหาวิธีปรับขนาดและดูแลการทดสอบ Cypress ของคุณให้ดีที่สุด คุณสามารถเลือกที่จะจัดระเบียบไฟล์ของคุณตามหน้า คุณลักษณะ หรือรูปแบบการจัดกลุ่มอื่นๆ ได้ ดังนั้นคุณจึงรู้อยู่เสมอว่าจะค้นหาไฟล์ทดสอบที่มีอยู่จากที่ใด และตำแหน่งที่จะเพิ่มไฟล์ใหม่เมื่อนักพัฒนามีส่วนสนับสนุนโค้ดเบสของคุณมากขึ้น

เมื่อ UI เปลี่ยนไป คุณสามารถใช้วัตถุประเภทใดประเภทหนึ่ง (เช่น ตัวเลือกการอ่านและเขียน) เพื่อรักษาตัวเลือกแอตทริบิวต์ "data-" ของคุณให้กับองค์ประกอบหลักของแต่ละหน้าที่คุณต้องการยืนยันหรือโต้ตอบภายในการทดสอบหน่วยและ Cypress การทดสอบ หากคุณพบว่าตัวเองเริ่มใช้ค่าที่กำหนดเองมากเกินไปสำหรับสิ่งต่าง ๆ เช่น ค่าการหมดเวลาสำหรับคำสั่ง Cypress ของคุณ อาจถึงเวลาแล้วที่จะต้องตั้งค่าวัตถุที่เต็มไปด้วยค่ามาตราส่วน เพื่อให้คุณสามารถอัปเดตค่าเหล่านั้นได้ในที่เดียว

เมื่อสิ่งต่างๆ เปลี่ยนไปในการทดสอบ frontend UI, backend API และ Cypress คุณควรพิจารณาหาวิธีดูแลรักษาการทดสอบเหล่านี้ให้ง่ายขึ้นอยู่เสมอ มีสถานที่น้อยกว่าในการอัปเดตและทำผิดพลาด และความคลุมเครือน้อยลงเกี่ยวกับตำแหน่งที่จะวางสิ่งต่าง ๆ สร้างความแตกต่างอย่างมากเนื่องจากนักพัฒนาจำนวนมากเพิ่มหน้าใหม่ คุณสมบัติ และ (หลีกเลี่ยงไม่ได้) การทดสอบ Cypress ลงที่ถนน

สนใจโพสต์เพิ่มเติมเกี่ยวกับ Cypress หรือไม่? ลองดูบทความต่อไปนี้:

  • สิ่งที่ต้องพิจารณาเมื่อเขียนการทดสอบ E2E
  • ภาพรวม 1,000 ฟุตของการเขียนแบบทดสอบ Cypress
  • TypeScript ทุกสิ่งในการทดสอบ Cypress ของคุณ
  • การจัดการกับกระแสอีเมลในการทดสอบ Cypress
  • การรวมการทดสอบ Cypress กับ Docker, Buildkite และ CICD