คู่มือแบบรวดเร็วในการเขียนทดสอบแบบ end-to-end ด้วย jest-puppeteer
บทความนี้ให้คำแนะนำในการเขียนการทดสอบ end-to-end ที่มีประสิทธิภาพด้วย jest-puppeteer ซึ่งเน้นที่กระบวนการตั้งค่า API ที่ใช้บ่อย และสถานการณ์ทดสอบเชิงปฏิบัติด้วยแอปพลิเคชัน to-do ที่เรียบง่ายเป็นตัวอย่าง
ในฐานะส่วนหนึ่งของความมุ่งมั่นของเราในการรับรองคุณภาพของ Logto และการปรับปรุงอย่างต่อเนื่อง เราใช้ jest-puppeteer สำหรับการทดสอบอัตโนมัติ end-to-end ซึ่งช่วยให้เราสามารถทำการพัฒนา Logto ได้อย่างรวดเร็วโดยไม่มีการขัดจังหวะ
ในบทความนี้ เราจะแบ่งปันประสบการณ์ของเรากับการเขียนสคริปต์ทดสอบ jest-puppeteer ที่มีประสิทธิภาพโดยใช้แอป to-do ที่เรียบง่ายเป็นตัวอย่าง เป้าหมายของเราคือช่วยให้คุณเริ่มต้นเขียนโค้ดทดสอบ jest-puppeteer สำหรับโครงการของคุณเองได้อย่างรวดเร็ว
บทนำสู่การทดสอบ end-to-end ด้วย jest-puppeteer
การทดสอบ end-to-end เป็นวิธีการเพื่อให้แน่ใจว่าแอปพลิเคชันของคุณทำงานอย่างถูกต้องจากมุมมองของผู้ใช้ เพื่อตอบสนองต่อความต้องกา รนี้ เราใช้เครื่องมือจำเป็นสองตัว: Jest และ Puppeteer
Jest เป็นเฟรมเวิร์กการทดสอบ JavaScript ที่เป็นที่นิยม ให้ API ที่ใช้งานง่ายสำหรับการเขียนการทดสอบและการยืนยันความคาดหวัง ใช้กันอย่างแพร่หลายสำหรับการทดสอบหน่วยและการรวม
Puppeteer เป็นไลบรารี Node.js ที่พัฒนาโดยทีม Chrome ซึ่งให้ API ระดับสูงสำหรับการควบคุมตัวบราวเซอร์ที่ไม่มีส่วนหัวของ Chrome หรือ Chromium ทำให้เป็นตัวเลือกที่เหมาะสมสำหรับการตอบสนองการโต้ตอบเบราว์เซอร์ในทดสอบ end-to-end
jest-puppeteer เป็นการตั้งค่าเริ่มต้นของ Jest ที่ช่วยให้ทดสอบ end-to-end ด้วย Puppeteer ให้ API ที่เรียบง่ายสำหรับเปิดใช้งานอินสแตนซ์เบราว์เซอร์ใหม่และโต้ตอบกับเว็บหน้า
ตอนนี้คุณมีความเข้าใจพื้นฐานของเครื่องมือจำเป็นแล้ว มาดำดิ่งเข้าไปในกระบวนการตั้งค่าสภาพแวดล้อมการทดสอ บของคุณ
การตั้งค่าสภาพแวดล้อมการทดสอบของคุณ
การตั้งค่าสภาพแวดล้อมการทดสอบสำหรับการทดสอบ end-to-end ด้วย jest-puppeteer เป็นกระบวนการง่ายๆที่เกี่ยวข้องกับสามขั้นตอนหลัก:
- ติดตั้ง dependencies
ในโครงการของคุณ (หรือสร้างโครงการใหม่) เปิด terminal และรันคำสั่ง:
จากนั้นไปที่ node_modules/puppeteer
และติดตั้ง Chromium (ซึ่งเป็นสิ่งจำเป็นสำหรับ Puppeteer):
- กำหนดค่า Jest
ต่อมา คุณต้องกำหนดค่า Jest เพื่อทำงานร่วมกับ Puppeteer อย่างราบรื่น
สร้างไฟล์กำหนดค่า Jest (เช่น jest.config.js
) ในไดเรกทอรีโฟลเดอร์หลักของโครงการของคุณถ้าคุณยังไม่มี ในไฟล์กำหนดค่านี้ ระบุ jest-puppeteer เป็น preset:
คุณสามารถปรับแต่การตั้งค่า Jest อื่นๆในไฟล์นี้ได้ตามต้องการ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่า Jest โปรดดูที่ Jest configuration
- เขียนการทดสอบของคุณ
สร้างไฟล์การทดสอบในโครงการของคุณ โดยทั่วไปแล้วชื่อไฟล์เหล่านี้จะลงท้ายด้วย .test.js
Jest จะค้นหาและเรียกใช้ไฟล์การทดสอบเหล่านี้โดยอัตโนมัติ
นี่คือตัวอย่างจาก Jest doc:
จากนั้นรันคำสั่งเพื่อเรียกใช้การทดสอบ:
เมื่อทำตามสามขั้นตอนนี้ คุณจะมีสภาพแวดล้อมการทดสอบที่กำหนดค่าอย่างดีเพื่อดำเนินการทดสอบ end-to-end โดยใช้ jest-puppeteer
อย่างไรก็ตาม โปรดทราบว่าตัวอย่างนี้เป็นเพียงพื้นฐานเท่านั้น สำหรับข้อมูลที่ละเอียดยิ่งขึ้นเกี่ยวกับการกำหนดค่าสภาพแวดล้อม โปรดดูเอกสารที่เกี่ยวข้อง:
API ที่ใช้บ่อย
ในขั้นตอนต่อไปนี้ เราจะใช้ API ที่จัดให้โดย Jest, Puppeteer และ jest-puppeteer เพื่อช่วยเราในการทดสอบของเรา
Jest มี API ที่หลักๆใช้สำหรับจัดระเบียบการทดสอบและยืนยันค่าที่คาดหวัง คุณสามารถสำรวจรายละเอียดเฉพาะได้ที่ เอกสาร
API ของ Puppeteer ถูกออกแบบมาเพื่อการโต้ตอบกับบราวเซอร์เป็นหลัก และการใช้งานในการทดสอบอาจไม่ได้เป็นเรื่องง่ายเสมอไป คุณสามารถดูเอกสารของ Puppeteer เพื่อทำความเข้าใจฟังก์ชั่นที่มี ในตัวอย่างการทดสอบถัดไปของเรา เราจะครอบคลุมการใช้งานบ่อย
เนื่องจาก API ของ Puppeteer ไม่ได้ถูกออกแบบมาเพื่อการทดสอบโดยเฉพาะ การใช้พวกเขาในการเขียนทดสอบอาจท้าทาย เพื่อทำให้กระบวนการนี้ง่ายขึ้น jest-puppeteer มีไลบรารีที่ฝังอยู่ชื่อว่า expect-puppeteer
ซึ่งมี API ที่ย่อและใช้งานง่าย โดยไลบรารีนี้ถูกอธิบายไว้ใน เอกสาร ของมัน ซึ่งแนะนำฟีเจอร์ต่างๆ ที่มีประโยชน์ ดังแสดงในตัวอย่างด้านล่าง:
ในตัวอย่างที่จะตามมา เราจะรวม API ที่จัดให้โดยไลบรารีเหล่านี้เพื่อทำให้สถานการณ์การทดสอบของเราสมบูรณ์
ถึงเวลาเริ่มเรียนรู้วิธีการเขียนโค้ดทดสอบโดยใช้แอป to-do ของเรา
แอป to-do ง่ายๆ
สมมติว่าเรามีแอป to-do ง่ายๆ รันอยู่ที่ http://localhost:3000
โค้ด HTML หลักสำหรับแอปนี้คือ:
เมื่อทำการทดสอบ end-to-end เรามีเป้าหมายที่จะครอบคลุมสถานการณ์ดังนี้:
- เมื่อเราเข้าถึง URL ของแอป แอปและข้อมูลควรโหลดอย่างถูกต้อง
- ปุ่มตรวจสอบของรายการควรคลิกได้
- การคลิกลิงก์ภายนอกภายในบันทึกรายการควรเปิดลิงก์ในแท็บใหม่
- สามารถเพิ่มรายการจากฟอร์มได้
ต่อมา เราจะเขียนโค้ดทดสอบเพื่อยืนยันสถานการณ์เหล่านี้
คาดว่าแอปและข้อมูลของมันได้โหลดแล้ว
เราถือว่าแอปได้โหลดเรียบร้อยเมื่อมีเงื่อนไขดังต่อไปนี้พบ:
- ควรมีองค์ประกอบที่มี ID "app" บนเพจหลังจากเข้าถึง URL ของแอป
- ข้อมูลภายในแอปควรแสดงผลอย่างถูกต้อง
ดังนั้น เราได้เขียนโค้ดทดสอบดังนี้:
ในโค้ด:
page.goto
เทียบเท่ากับการป้อน "http://localhost:3000" ในบราวเซอร์ ทำให้สามารถเปิดไปยัง URL ใด ๆpage.waitForSelector
ใช้เพื่อรอให้ตัวเลือก CSS เฉพาะตรงกับองค์ประกอบ โดยมีระยะเวลารอเริ่มต้น 30 วินาทีexpect(page).toMatchElement
มาจากไลบรารีexpect-puppeteer
มันคล้ายกับpage.waitForSelector
แต่ยังสนับสนุนการจับคู่ต�ะกูลข้อความภายในองค์ประกอบ นอกจากนี้ การหมดเวลาการรอเริ่มต้นสำหรับtoMatchElement
มีเพียง 500 มิลลิวินาที
มันอาจดูเหมือนสมบูรณ์แบบเมื่อมองในครั้งแรก แต่เมื่อรันการทดสอบนี้และเปิดในสิ่งแวดล้อม CI บางครั้งก็ล้มเหลวหลังจากทำซ้ำหลายครั้ง ข้อความล้มเหลวระบุว่า:
โดยพิจารณาข้อมูลความล้มเหลวและข้อเท็จจริงที่ว่าการทดสอบนี้ผ่านการทดสอบมากที่สุดของเวลา เราสามารถอนุมานได้ว่าข้อมูลที่ถูกร้องขอโดยแอปอาจไม่เสมือนจริงที่จะคืนภายใน 500 มิลลิอาเตอรี่แรกหลังจากแอปโหลด ดังนั้นเราต้องการรอให้ข้อมูลโหลดก่อนทำการยืนยัน
โดยทั่วไป มีสองวิธีทั่วไปเพื่อให้บรรลุนี้:
- เพิ่มระยะเวลาการรอการยืนยัน
จากข้อความแสดงข้อผิดพลาด เราสามารถเห็นว่าระยะ เวลาการรอเริ่มต้นสำหรับ toMatchElement
ถูกตั้งไว้ที่ 500 มิลลิวินาที เราสามารถเพิ่มระยะเวลาการรอโดยเพิ่มตัวเลือก timeout
ใส่ฟังก์ชั่นดังนี้:
วิธีการนี้สามารถลดการเกิดของการทดสอบที่ล้มเหลวในบางระดับ แต่ไม่ได้แก้ปัญหาความจริงทั้งหมดเพราะเราไม่สามารถทราบได้อย่างแน่นอนว่าต้องใช้เวลาเท่าใดเพื่อดึงข้อมูล
ด้วยเหตุนี้เราจะใช้วิธีการนี้เมื่อเรามั่นใจในเวลาที่ต้องการรอเท่านั้น เช่นในสถานการณ์เช่น "tooltip ปรากฏหลังจากเลื่อนเมาส์บนองค์ประกอบเกิน 2 วินาที”
- รอจนจบการร้องขอของเครือข่ายให้เสร็จก่อนทำการยืนยัน
นี่คือวิธีการที่ถูกต้อง ถึงแม้ว่าเราอาจไม่ทราบระยะเวลาการร้องขอข้อมูล แต่การรอให้การร้องขอเครือข่ายเสร็จสิ้นจะเป็นตัวเลือกที่ปลอดภัยเสมอ ในขั้นตอนนี้ เราสามารถใช้ page.waitForNavigation({ waitUntil: 'networkidle0' })
เพื่อรอให้การร้องขอเครือข่ายเสร็จสิ้น:
ด้วยวิธีนี้ ก่อนที่เราจะทำการยืนยันเพื่อทดสอบแอปและข้อมูลที่โหลดแล้ว เราจึงสามารถมั่นใจได้ว่าการร้องขอเครือข่ายของเราได้สิ้นสุดแล้ว ซึ่งทำให้การทดสอบออกมาตรงตามผลลัพธ์ที่ถูกต้องเสมอ
คาดว่าจะสามารถคลิกปุ่มเฉพาะได้
ถัดไปเรากำลังทดสอบฟังก์ชั่นการคลิกปุ่มตรวจสอบภายในรายการ
ในแอปตัวอย่าง เราสังเกตเห็นว่ารายการทั้งหมดมีโครงสร้างเดียวกัน ปุ่มตรวจสอบของพวกเขามีตัวเลือก CSS li[class$=item] > button
และข้อความในปุ่มเป็น"Check"ซึ่งหมายความว่าเราไม่สามารถระบุได้โดยตรงว่าจะคลิกปุ่มตรวจสอบของรายการใด ดังนั้นเราต้องการวิธีการใหม่
สมมติว่าเราต้องการคลิกปุ่มตรวจสอบของรายการ "Read Logto get-started document" เราสามารถแยกออกเป็นสองขั้นตอนนี้:
- ประการแรก รับการอ้างอิงถึงรายการนั้น
- จากนั้น คลิกปุ่มตรวจสอบที่อยู่ในรายการนั้น
ตามที่แสดงในโค้ด เราใช้ฟังก์ชัน toMatchElement
เพื่อจับคู่รายการที่มี itemName
ตั้งไว้เป็น "Read Logto get-started document" จากนั้นเราใช้การอ้างอิง readDocItem
เพื่อคลิกปุ่มที่มีข้อความ 'Check' อยู่ข้างล่าง
สิ่งสำคัญที่ควรสังเกตเมื่อเราได้รับ readDocItem
ตัวเลือก CSS ที่ใช้คือ li[class$=item]:has(div[class$=itemName])
ตัวเลือกนี้จับคู่กับองค์ประกอบ li
รากของรายการ ไม่ใช่ itemName
ภายในเพราะเราต้องการคลิกปุ่มภายใต้แท็ก li
ภายหลัง
การใช้งาน expect(readDocItem).toClick
คล้ายกับ toMatchElement
ในโค้ดตัวอย่าง เราใช้ { text: 'Check' }
เพื่อจับคู่กับ textContent
ของปุ่มเพิ่มเติม อย่างไรก็ตาม คุณสา มารถเลือกว่าจะจับคู่เนื้อหาข้อความของปุ่มหรือไม่ตามความต้องการของคุณ
คาดว่าลิงก์ภายนอกจะเปิดในแท็บใหม่
ถัดไป เราต้องการทดสอบว่าเราสามารถเปิดลิงก์ภายนอกในแท็บใหม่เมื่อคลิกที่มันในบันทึกรายการ
ในแอปตัวอย่าง เราพบว่ารายการ "Read Logto get-started document" มีลิงก์ภายนอกไปยังเอกสาร Logto อยู่ภายในหมายเหตุของมัน นี่คือโค้ดทดสอบของเรา:
ในโค้ด เราใช้ toClick
เพื่อคลิกลิงก์ใน itemNotes
หลังจากนั้น เราใช้ browser.waitForTarget
เพื่อควบคุมแท็บที่เปิดใหม่ด้วย URL "https://docs.logto.io/docs/tutorials/get-started."
หลังจากที่ได้หน้าเพจนี้ เราใช้ target.page()
เพื่อรับ instance ของหน้าเอกสาร Logto และตรวจสอบเพิ่มเติมว่าหน้าได้โหลดหรือไม่
นอ กจากนี้ จำไว้ว่าต้องปิดหน้าเพจที่เปิดใหม่หลังจากทดสอบเสร็จสิ้น
คาดว่าจะสร้างรายการจากฟอร์ม
ตอนนี้เราต้องการทดสอบฟังก์ชั่นการเพิ่มรายการ
เราต้องกรอกชื่อรายการและบันทึกรายการในฟอร์ม คลิกปุ่ม "Add" และตรวจสอบว่าข้อมูลที่เราเพิ่มปรากฏขึ้นในรายการหรือไม่:
คุณจะสังเกตเห็นว่าเราใช้ฟังก์ชั่น expect(page).toFill(inputSelector, content)
เพื่อกรอกเนื้อหาในฟอร์ม ฟังก์ชันนี้แทนที่เนื้อหาทั้งหมดใน input ด้วย content
ถ้าคุณต้องการเพิ่มตัวอักษรลงใน input โดยไม่แทนที่เนื้อหาที่มีอยู่ คุณสามารถใช้ page.type(selector, content)
เพื่อเพิ่มเนื้อหาที่ต้องการลงใน input ได้โดยตรง
อย่างไรก็ตาม เมื่อเรามีเขตข้อมูลหลายช่องในแบบฟอร์ม เรียก toFill
หลายครั้งก็ไม่สะดวก ในกรณีนี้ เราสามารถใช้แนวทางต่อไปนี้เพื่อกรอกเนื้อหาทั้งหมดในครั้งเดียว:
ชื่อคีย์ของวัตถุที่ให้คือคุณสมบัติชื่อขององค์ประกอบ input
สรุป
เราได้ครอบคลุมตัวอย่างง่ายๆ เพื่อแนะนำความต้องการการทดสอบทั่วไปและเทคนิคการเขียนโค้ดตามที่จำเป็นเมื่อใช้ jest-puppeteer สำหรับการทดสอบ end-to-end เราหวังว่ามันสามารถช่วยให้คุณเข้าใจกับการเขียนโค้ดทดสอบ jest-puppeteer เบื้องต้นได้อย่างรวดเร็ว