Built-in Assertions ของ Playwright
assertion คือจุดที่ test บอกว่า 'สิ่งที่ควรเกิดขึ้นเกิดจริงหรือไม่' บทนี้จะช่วยให้คุณเลือก assertion ให้ตรงกับสิ่งที่อยากพิสูจน์ และใช้พลัง auto-retrying ของ Playwright ให้คุ้มที่สุด
จบบทนี้แล้วคุณจะ...
- ใช้ built-in assertions ที่พบบ่อยใน Playwright ได้ถูกสถานการณ์
- เข้าใจความต่างระหว่าง auto-retrying assertions และการเช็กค่าธรรมดา
- เลือก assertion ที่สื่อ intent ชัด เช่น
toBeVisible,toHaveText,toHaveURL - ใช้ soft assertions อย่างระวังและรู้ว่าเหมาะเมื่อไร
- ลด false failures ที่เกิดจากการเลือก assertion ไม่ตรงกับพฤติกรรมจริงของหน้า
เพราะมันไม่ได้เช็กแค่ “ตอนนี้” แต่พยายามรอให้หน้าเข้าสู่สภาพที่คาดหวังก่อน
Playwright assertions มี auto-retrying ซึ่งต่างจาก assertion ธรรมดาที่เช็กครั้งเดียวแล้วจบ
await expect(page.getByText('Success')).toBeVisible();
expect(value).toBe(5);
จุดต่างคือบรรทัดแรกจะพยายามรอจน element ปรากฏหรือ timeout ขณะที่บรรทัดหลังเช็กค่าทันที
ใช้เมื่อสิ่งที่อยากพิสูจน์คือ state ของ page ไม่ใช่ element เดี่ยว
await expect(page).toHaveTitle('Shopping Cart');
await expect(page).toHaveTitle(/Shopping/);
await expect(page).toHaveURL('https://example.com/cart');
await expect(page).toHaveURL(/.*cart/);
เหมาะกับสถานการณ์เช่น:
- ตรวจว่า redirect สำเร็จแล้ว
- ตรวจว่าอยู่ในหน้าที่ถูกต้อง
- ตรวจว่า title เปลี่ยนตาม flow ที่คาด
นี่คือกลุ่มที่ใช้บ่อยที่สุดใน UI testing
await expect(page.getByText('Welcome')).toBeVisible();
await expect(page.getByText('Loading...')).toBeHidden();
await expect(page.getByRole('button', { name: 'Submit' })).toBeEnabled();
await expect(page.getByRole('button', { name: 'Submit' })).toBeDisabled();
await expect(page.getByRole('checkbox', { name: 'Accept' })).toBeChecked();
await expect(page.getByRole('checkbox', { name: 'Accept' })).not.toBeChecked();
ข้อสังเกต:
toBeVisible()ดีเมื่อผู้ใช้ควรเห็น element จริง ๆtoBeHidden()เหมาะกับ loading, modal ที่ปิดแล้ว หรือ error ที่ไม่ควรแสดงtoBeEnabled()/toBeDisabled()ใช้พิสูจน์ business rule ผ่านสถานะของปุ่มได้ดีมาก
ใช้ให้ตรงกับสิ่งที่คุณอยากยืนยัน ไม่ต้องแรงเกินจำเป็น
await expect(page.getByTestId('status')).toHaveText('Order confirmed');
await expect(page.getByTestId('status')).toContainText('confirmed');
await expect(page.getByTestId('price')).toHaveText(/฿[\d,]+/);
แนวคิดสำคัญ:
toHaveText()เหมาะเมื่อข้อความควรตรงจริงtoContainText()เหมาะเมื่อมี prefix/suffix หรือข้อความอาจยาวกว่าที่สนใจ- regex เหมาะกับค่าที่แปรผัน เช่น ราคา เลขออเดอร์ วันที่
ใช้เมื่อหน้าไม่ได้เปลี่ยนแค่การมองเห็น แต่เปลี่ยนค่าภายในด้วย
await expect(page.getByLabel('Email')).toHaveValue('[email protected]');
await expect(page.getByRole('link', { name: 'Docs' }))
.toHaveAttribute('href', '/docs');
await expect(page.getByTestId('alert')).toHaveClass(/danger/);
ตัวอย่างสถานการณ์:
- ฟอร์ม autofill แล้วต้องเช็กค่าช่อง input
- ลิงก์ควรชี้ไป URL ที่ถูก
- alert ควรมี class ตามสถานะ success/error
ดีมากเวลาทดสอบ list, table, cart หรือ search results
await expect(page.getByRole('listitem')).toHaveCount(3);
await expect(page.getByRole('listitem')).toHaveCount(0);
ข้อดีคือมันช่วยให้เราพิสูจน์ behavior เชิงปริมาณได้ตรง ๆ โดยไม่ต้องดึง array มานับเอง
ใช้ได้ แต่ไม่ควรใช้จน test ซ่อนปัญหา
await expect.soft(page.getByTestId('status')).toHaveText('Active');
await expect.soft(page.getByTestId('balance')).toContainText('฿1,000');
เหมาะเมื่อ:
- คุณกำลังตรวจหลาย field บน report เดียว
- อยากเก็บ failure หลายจุดในรอบเดียว
ไม่ค่อยเหมาะเมื่อ:
- ขั้นต่อไปของ test พึ่งพาค่าที่ assertion ก่อนหน้านี้ยืนยันอยู่
await expect(page.getByRole('button', { name: 'Checkout' }))
.toBeEnabled({ message: 'Checkout button should be enabled after adding items' });
message ที่ดีควรบอก business expectation ไม่ใช่แค่บอกชื่อ element ซ้ำ
แบบฝึก: เลือก assertion ให้ตรงกับสถานการณ์
Scenario 1: Login fail
หลังกรอก email/password ผิดแล้วกด Submit ต้องเห็นข้อความ Invalid email or password
ดูคำตอบแนะนำ
await expect(page.getByText('Invalid email or password')).toBeVisible();
Scenario 2: ตะกร้าสินค้า
หลังเพิ่มสินค้า 3 ชิ้น ต้องมีรายการ 3 ตัว และปุ่ม Checkout ต้องกดได้
ดูคำตอบแนะนำ
await expect(page.getByRole('listitem')).toHaveCount(3);
await expect(page.getByRole('button', { name: 'Checkout' })).toBeEnabled();
Scenario 3: Profile page
หลัง login ต้องไปที่ URL /profile และเห็นชื่อ John Doe
ดูคำตอบแนะนำ
await expect(page).toHaveURL(/.*profile/);
await expect(page.getByText('John Doe')).toBeVisible();
Scenario 4: Form state
ช่อง Email ต้องมีค่า [email protected] และ checkbox Remember me ต้องถูกเลือกไว้
ดูคำตอบแนะนำ
await expect(page.getByLabel('Email')).toHaveValue('[email protected]');
await expect(page.getByRole('checkbox', { name: 'Remember me' })).toBeChecked();
สิ่งที่ต้องเอาติดตัวไปจากบทนี้
- จุดเด่นใหญ่ของ Playwright assertions คือ auto-retrying
- เลือก assertion ให้ตรงกับสิ่งที่อยากพิสูจน์ เช่น visibility, text, URL, value หรือ count
toHaveText()และtoContainText()ให้ความเข้มงวดไม่เท่ากัน ควรเลือกให้พอดี- soft assertions ใช้ได้เมื่ออยากเก็บ failure หลายจุด แต่ไม่ควรใช้จน test อ่านยากหรือซ่อนปัญหา
- assertion ที่ดีช่วยให้ test ทั้งเสถียรและสื่อความหมายชัดขึ้นพร้อมกัน