• การทดสอบ
  • การทดสอบ end-to-end
  • การทดสอบการรวม
  • การพัฒนา
  • ผลิตภาพ
  • jest
  • puppeteer

คู่มือแบบรวดเร็วในการเขียนทดสอบแบบ end-to-end ด้วย jest-puppeteer

บทความนี้ให้คำแนะนำในการเขียนการทดสอบ end-to-end ที่มีประสิทธิภาพด้วย jest-puppeteer ซึ่งเน้นที่กระบวนการตั้งค่า API ที่ใช้บ่อย และสถานการณ์ทดสอบเชิงปฏิบัติด้วยแอปพลิเคชัน to-do ที่เรียบง่ายเป็นตัวอย่าง

Yijun
Yijun
Developer

ในฐานะส่วนหนึ่งของความมุ่งมั่นของเราในการรับรองคุณภาพของ 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 เป็นกระบวนการง่ายๆที่เกี่ยวข้องกับสามขั้นตอนหลัก:

  1. ติดตั้ง dependencies

ในโครงการของคุณ (หรือสร้างโครงการใหม่) เปิด terminal และรันคำสั่ง:

จากนั้นไปที่ node_modules/puppeteer และติดตั้ง Chromium (ซึ่งเป็นสิ่งจำเป็นสำหรับ Puppeteer):

  1. กำหนดค่า Jest

ต่อมา คุณต้องกำหนดค่า Jest เพื่อทำงานร่วมกับ Puppeteer อย่างราบรื่น

สร้างไฟล์กำหนดค่า Jest (เช่น jest.config.js) ในไดเรกทอรีโฟลเดอร์หลักของโครงการของคุณถ้าคุณยังไม่มี ในไฟล์กำหนดค่านี้ ระบุ jest-puppeteer เป็น preset:

คุณสามารถปรับแต่การตั้งค่า Jest อื่นๆในไฟล์นี้ได้ตามต้องการ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่า Jest โปรดดูที่ Jest configuration

  1. เขียนการทดสอบของคุณ

สร้างไฟล์การทดสอบในโครงการของคุณ โดยทั่วไปแล้วชื่อไฟล์เหล่านี้จะลงท้ายด้วย .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 เรามีเป้าหมายที่จะครอบคลุมสถานการณ์ดังนี้:

  1. เมื่อเราเข้าถึง URL ของแอป แอปและข้อมูลควรโหลดอย่างถูกต้อง
  2. ปุ่มตรวจสอบของรายการควรคลิกได้
  3. การคลิกลิงก์ภายนอกภายในบันทึกรายการควรเปิดลิงก์ในแท็บใหม่
  4. สามารถเพิ่มรายการจากฟอร์มได้

ต่อมา เราจะเขียนโค้ดทดสอบเพื่อยืนยันสถานการณ์เหล่านี้

คาดว่าแอปและข้อมูลของมันได้โหลดแล้ว

เราถือว่าแอปได้โหลดเรียบร้อยเมื่อมีเงื่อนไขดังต่อไปนี้พบ:

  1. ควรมีองค์ประกอบที่มี ID "app" บนเพจหลังจากเข้าถึง URL ของแอป
  2. ข้อมูลภายในแอปควรแสดงผลอย่างถูกต้อง

ดังนั้น เราได้เขียนโค้ดทดสอบดังนี้:

ในโค้ด:

  • page.goto เทียบเท่ากับการป้อน "http://localhost:3000" ในบราวเซอร์ ทำให้สามารถเปิดไปยัง URL ใด ๆ
  • page.waitForSelector ใช้เพื่อรอให้ตัวเลือก CSS เฉพาะตรงกับองค์ประกอบ โดยมีระยะเวลารอเริ่มต้น 30 วินาที
  • expect(page).toMatchElement มาจากไลบรารี expect-puppeteer มันคล้ายกับ page.waitForSelector แต่ยังสนับสนุนการจับคู่ต�ะกูลข้อความภายในองค์ประกอบ นอกจากนี้ การหมดเวลาการรอเริ่มต้นสำหรับ toMatchElement มีเพียง 500 มิลลิวินาที

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

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

โดยทั่วไป มีสองวิธีทั่วไปเพื่อให้บรรลุนี้:

  1. เพิ่มระยะเวลาการรอการยืนยัน

จากข้อความแสดงข้อผิดพลาด เราสามารถเห็นว่าระยะเวลาการรอเริ่มต้นสำหรับ toMatchElement ถูกตั้งไว้ที่ 500 มิลลิวินาที เราสามารถเพิ่มระยะเวลาการรอโดยเพิ่มตัวเลือก timeout ใส่ฟังก์ชั่นดังนี้:

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

ด้วยเหตุนี้เราจะใช้วิธีการนี้เมื่อเรามั่นใจในเวลาที่ต้องการรอเท่านั้น เช่นในสถานการณ์เช่น "tooltip ปรากฏหลังจากเลื่อนเมาส์บนองค์ประกอบเกิน 2 วินาที”

  1. รอจนจบการร้องขอของเครือข่ายให้เสร็จก่อนทำการยืนยัน

นี่คือวิธีการที่ถูกต้อง ถึงแม้ว่าเราอาจไม่ทราบระยะเวลาการร้องขอข้อมูล แต่การรอให้การร้องขอเครือข่ายเสร็จสิ้นจะเป็นตัวเลือกที่ปลอดภัยเสมอ ในขั้นตอนนี้ เราสามารถใช้ page.waitForNavigation({ waitUntil: 'networkidle0' }) เพื่อรอให้การร้องขอเครือข่ายเสร็จสิ้น:

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

คาดว่าจะสามารถคลิกปุ่มเฉพาะได้

ถัดไปเรากำลังทดสอบฟังก์ชั่นการคลิกปุ่มตรวจสอบภายในรายการ

ในแอปตัวอย่าง เราสังเกตเห็นว่ารายการทั้งหมดมีโครงสร้างเดียวกัน ปุ่มตรวจสอบของพวกเขามีตัวเลือก CSS li[class$=item] > button และข้อความในปุ่มเป็น"Check"ซึ่งหมายความว่าเราไม่สามารถระบุได้โดยตรงว่าจะคลิกปุ่มตรวจสอบของรายการใด ดังนั้นเราต้องการวิธีการใหม่

สมมติว่าเราต้องการคลิกปุ่มตรวจสอบของรายการ "Read Logto get-started document" เราสามารถแยกออกเป็นสองขั้นตอนนี้:

  1. ประการแรก รับการอ้างอิงถึงรายการนั้น
  2. จากนั้น คลิกปุ่มตรวจสอบที่อยู่ในรายการนั้น

ตามที่แสดงในโค้ด เราใช้ฟังก์ชัน 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 เบื้องต้นได้อย่างรวดเร็ว