Assertions และ Waiting Strategies
จบบทนี้แล้วคุณจะ...
- อธิบายได้ว่า assertion คืออะไร และมันตรวจสอบอะไรบ้าง
- แยกแยะ hard assertion vs soft assertion ได้
- เข้าใจปัญหาของ timing ใน automation และทำไม sleep() ถึงเป็นทางเลือกที่แย่
- รู้จัก explicit wait, implicit wait, และ polling strategy
- ออกแบบ waiting strategy ที่เหมาะสมกับสถานการณ์ได้
Assertion คือ "คำยืนยัน" ที่ test ใช้ตรวจสอบว่าผลลัพธ์ตรงตามที่คาดหวังหรือไม่
ถ้า test เป็นเหมือนการทดลองวิทยาศาสตร์:
- ขั้นตอน = Action (คลิก, พิมพ์, navigate)
- Assertion = การตรวจสอบผลลัพธ์ (เห็นข้อความที่ถูกต้อง, element แสดงผล, ค่าเปลี่ยนไป)
Test ที่ไม่มี assertion เหมือนการทดลองที่ไม่ได้วัดผล — ไม่มีประโยชน์
ตัวอย่าง Assertions (concept level)
| สิ่งที่ตรวจสอบ | ตัวอย่าง |
|---|---|
| Element visible | หลังจาก login สำเร็จ, ชื่อผู้ใช้ต้องแสดงบนหน้าจอ |
| Text content | ข้อความ "สั่งซื้อสำเร็จ" ต้องปรากฏใน confirmation page |
| Value | ยอดรวมในตะกร้าต้องเป็น ฿12,900 |
| Count | จำนวนสินค้าในตะกร้าต้องเป็น 3 ชิ้น |
| State | ปุ่ม "ยืนยัน" ต้อง disabled จนกว่าจะกรอกข้อมูลครบ |
| URL | หลัง redirect ต้องอยู่ที่หน้า /dashboard |
Hard Assertion
- หยุดทันที เมื่อ assertion fail
- Test case จบ ณ จุดนั้น ไม่ทำต่อ
- เหมาะกับ: critical check ที่ถ้าไม่ผ่านแล้วทำต่อไปก็ไม่มีประโยชน์
ตัวอย่าง: test login → ถ้า login ไม่สำเร็จ ก็ไม่มีประโยชน์จะเช็คหน้า dashboard ต่อ
Soft Assertion
- เก็บ failure ไว้ แล้วทำต่อจนจบ test case
- รายงาน failure ทั้งหมดทีเดียวตอนท้าย
- เหมาะกับ: validation หลายจุดในหน้าเดียว ที่อยากรู้ทุก failure พร้อมกัน
ตัวอย่าง: test หน้า profile → เช็คชื่อ, อีเมล, เบอร์โทร, ที่อยู่ — อยากรู้ว่าอะไรผิดบ้างทั้งหมด ไม่ใช่เจอชื่อผิดแล้วหยุดเลย
เมื่อไหร่ใช้อะไร?
| สถานการณ์ | ใช้ |
|---|---|
| Login / authentication check | Hard |
| การเช็ค prerequisite ก่อนทำ step ถัดไป | Hard |
| Validation หลายช่องในฟอร์มเดียว | Soft |
| เช็ค UI elements หลายตัวในหน้าเดียว | Soft |
เรื่องเล่า: test ที่ใช้ sleep(5000) แล้วยังพัง
ทีม QA ของบริษัท e-commerce แห่งหนึ่งมี automation test ที่ทดสอบการค้นหาสินค้า ขั้นตอนคือ:
- พิมพ์คำค้นหา
- กดปุ่มค้นหา
- sleep(5000) — รอ 5 วินาที
- เช็คว่าผลลัพธ์แสดง
Test ผ่านบนเครื่อง dev ทุกครั้ง แต่พอรันบน CI server กลับ fail 30% ของเวลา
สาเหตุ: CI server ใช้ network ร่วมกับระบบอื่น บางครั้ง API response กลับมาใน 2 วินาที บางครั้ง 8 วินาที — sleep(5000) ไม่พอในบางรอบ
ทีมแก้โดยเพิ่มเป็น sleep(10000) — test ช้าลง แต่ก็ยังพังอยู่ดีเมื่อ network โหลดหนัก
ปัญหาที่แท้จริง: sleep() เป็นการ "เดาเวลา" — ถ้าเร็วกว่าที่เดาก็เสียเวลาเปล่า ถ้าช้ากว่าที่เดาก็ fail
Implicit Wait
- กำหนด timeout กลาง เช่น "ทุก element รอได้สูงสุด 10 วินาที"
- apply กับทุกการหา element โดยอัตโนมัติ
- ข้อดี: ตั้งครั้งเดียวใช้ทั้ง project
- ข้อเสีย: ไม่ยืดหยุ่น, อาจรอนานเกินจำเป็นสำหรับ element ที่ควรเจอเร็ว
Explicit Wait
- กำหนด condition ที่ต้องรอ เช่น "รอจนกว่า element จะ visible"
- กำหนด timeout เฉพาะจุด
- ข้อดี: ยืดหยุ่น, รอเท่าที่จำเป็น, อ่านเข้าใจว่ารออะไร
- ข้อเสีย: เขียน code มากขึ้น
Polling
- ตรวจสอบ condition ซ้ำ ๆ ทุก X milliseconds จนกว่าจะสำเร็จหรือ timeout
- เป็นกลไกภายในของ explicit wait
- ตัวอย่าง: เช็คทุก 500ms ว่า loading spinner หายไปหรือยัง
ทำไม sleep() ถึงแย่
| sleep() | Explicit Wait |
|---|---|
| รอเท่าเดิมทุกครั้ง แม้ element พร้อมแล้ว | รอเท่าที่จำเป็น |
| ถ้า element ช้ากว่าที่เดาก็ fail | มี timeout ที่เหมาะสม |
| ทำให้ test suite ช้าลงมาก | ทำให้ test suite เร็วที่สุดเท่าที่จะเป็นไปได้ |
| อ่าน code แล้วไม่รู้ว่ารออะไร | อ่าน code แล้วเข้าใจว่ารอ condition อะไร |
Timeout Strategy
หลักการตั้ง timeout:
- ค่า default: 10-30 วินาที (ขึ้นกับ application)
- API calls: 15-30 วินาที
- Page load: 30-60 วินาที
- File upload/download: 60-120 วินาที
- Never: ไม่ตั้ง timeout เลย (test อาจค้างไม่มีกำหนด)
ทีมแก้ปัญหาโดยเปลี่ยนจาก sleep(5000) เป็น:
"รอจนกว่า loading spinner จะหายไป และผลลัพธ์การค้นหาจะแสดง (timeout 30 วินาที)"
ผลลัพธ์:
- test ที่เคย fail 30% กลายเป็น fail 0%
- test ทำงานเร็วขึ้นเฉลี่ย 3 วินาทีต่อเคส (เพราะไม่ต้องรอ 5 วินาทีเมื่อ response กลับเร็ว)
- test suite 200 เคส ประหยัดเวลาได้ 10 นาทีต่อรอบ
โจทย์
ดู test pseudocode ต่อไปนี้ แล้ววิเคราะห์ปัญหาและแนะนำวิธีแก้
Test: ทดสอบการเพิ่มสินค้าลงตะกร้า
1. เปิดหน้าสินค้า
2. sleep(3000)
3. คลิกปุ่ม "เพิ่มลงตะกร้า"
4. sleep(2000)
5. assert: badge ตะกร้าแสดงเลข "1"
6. คลิกปุ่ม "ไปหน้าตะกร้า"
7. sleep(5000)
8. assert: ชื่อสินค้าแสดงในตะกร้า
9. assert: ราคาสินค้าถูกต้อง
10. assert: ปุ่ม "ชำระเงิน" enabled
คำถาม:
- test นี้มี sleep กี่จุด? รวมกี่วินาที?
- แต่ละ sleep รอ "อะไร" จริง ๆ?
- เขียนใหม่โดยใช้ explicit wait
- assertion ข้อ 5, 8, 9, 10 ควรเป็น hard หรือ soft?
ดูคำตอบแนะนำ
1. มี sleep 3 จุด รวม 10 วินาที (3+2+5) ทุกรอบ แม้ระบบจะเร็ว
2.
- sleep(3000) → รอหน้าสินค้า load เสร็จ
- sleep(2000) → รอ API add-to-cart ทำงานเสร็จ + UI update
- sleep(5000) → รอหน้าตะกร้า load เสร็จ
3. เขียนใหม่:
Test: ทดสอบการเพิ่มสินค้าลงตะกร้า
1. เปิดหน้าสินค้า
2. waitUntil: ปุ่ม "เพิ่มลงตะกร้า" visible (timeout 15s)
3. คลิกปุ่ม "เพิ่มลงตะกร้า"
4. waitUntil: badge ตะกร้าแสดงเลข "1" (timeout 10s)
5. assert: badge ตะกร้าแสดงเลข "1"
6. คลิกปุ่ม "ไปหน้าตะกร้า"
7. waitUntil: ชื่อสินค้าแสดงในตะกร้า (timeout 15s)
8. assert: ชื่อสินค้าแสดงในตะกร้า
9. assert: ราคาสินค้าถูกต้อง
10. assert: ปุ่ม "ชำระเงิน" enabled
4.
- ข้อ 5 (badge) → Hard assertion — ถ้า add-to-cart ไม่สำเร็จ ไปหน้าตะกร้าก็ไม่มีประโยชน์
- ข้อ 8, 9, 10 (ชื่อ, ราคา, ปุ่ม) → Soft assertion — อยู่ในหน้าเดียวกัน อยากรู้ว่าอะไรผิดบ้างทั้งหมด
สิ่งที่ต้องเอาติดตัวไปจากบทนี้
- Assertion คือหัวใจของ test — test ที่ไม่มี assertion ไม่มีประโยชน์
- Hard assertion หยุดทันที ใช้กับ critical checkpoint / Soft assertion ทำต่อ ใช้กับ validation หลายจุด
- sleep() คือศัตรูของ automation — ทำให้ test ช้าและ flaky
- Explicit wait รอเท่าที่จำเป็นตาม condition ที่ชัดเจน — เร็วกว่าและเสถียรกว่า
- ตั้ง timeout ที่เหมาะสม — ไม่สั้นเกินจน fail บ่อย ไม่ยาวเกินจน test ช้า