CupCode.cc
Section 04 · Module 02 · Lesson 02 · ~20–24 นาที

Built-in Assertions ของ Playwright

assertion คือจุดที่ test บอกว่า 'สิ่งที่ควรเกิดขึ้นเกิดจริงหรือไม่' บทนี้จะช่วยให้คุณเลือก assertion ให้ตรงกับสิ่งที่อยากพิสูจน์ และใช้พลัง auto-retrying ของ Playwright ให้คุ้มที่สุด

เวลาเรียน: 20–24 นาที ต้องรู้มาก่อน: Module 02, Lesson 01 - Playwright Locator Strategies รูปแบบ: อ่าน + ตัวอย่าง assertion + แบบฝึกสถานการณ์จริง

จบบทนี้แล้วคุณจะ...

  • ใช้ built-in assertions ที่พบบ่อยใน Playwright ได้ถูกสถานการณ์
  • เข้าใจความต่างระหว่าง auto-retrying assertions และการเช็กค่าธรรมดา
  • เลือก assertion ที่สื่อ intent ชัด เช่น toBeVisible, toHaveText, toHaveURL
  • ใช้ soft assertions อย่างระวังและรู้ว่าเหมาะเมื่อไร
  • ลด false failures ที่เกิดจากการเลือก assertion ไม่ตรงกับพฤติกรรมจริงของหน้า
1 · ทำไม assertions ของ Playwright ถึงน่าใช้

เพราะมันไม่ได้เช็กแค่ “ตอนนี้” แต่พยายามรอให้หน้าเข้าสู่สภาพที่คาดหวังก่อน

Playwright assertions มี auto-retrying ซึ่งต่างจาก assertion ธรรมดาที่เช็กครั้งเดียวแล้วจบ

await expect(page.getByText('Success')).toBeVisible();
expect(value).toBe(5);

จุดต่างคือบรรทัดแรกจะพยายามรอจน element ปรากฏหรือ timeout ขณะที่บรรทัดหลังเช็กค่าทันที

2 · Assertions สำหรับทั้งหน้า

ใช้เมื่อสิ่งที่อยากพิสูจน์คือ 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 ที่คาด
3 · Assertions สำหรับ element: มองเห็นได้ไหม ใช้งานได้ไหม

นี่คือกลุ่มที่ใช้บ่อยที่สุดใน 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 ผ่านสถานะของปุ่มได้ดีมาก
4 · Assertions สำหรับข้อความ

ใช้ให้ตรงกับสิ่งที่คุณอยากยืนยัน ไม่ต้องแรงเกินจำเป็น

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 เหมาะกับค่าที่แปรผัน เช่น ราคา เลขออเดอร์ วันที่
5 · Assertions สำหรับค่า, attribute และ class

ใช้เมื่อหน้าไม่ได้เปลี่ยนแค่การมองเห็น แต่เปลี่ยนค่าภายในด้วย

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
6 · `toHaveCount()` ใช้ตรวจรายการซ้ำ ๆ

ดีมากเวลาทดสอบ list, table, cart หรือ search results

await expect(page.getByRole('listitem')).toHaveCount(3);
await expect(page.getByRole('listitem')).toHaveCount(0);

ข้อดีคือมันช่วยให้เราพิสูจน์ behavior เชิงปริมาณได้ตรง ๆ โดยไม่ต้องดึง array มานับเอง

7 · Soft assertions ใช้เมื่อไร

ใช้ได้ แต่ไม่ควรใช้จน test ซ่อนปัญหา

await expect.soft(page.getByTestId('status')).toHaveText('Active');
await expect.soft(page.getByTestId('balance')).toContainText('฿1,000');

เหมาะเมื่อ:

  • คุณกำลังตรวจหลาย field บน report เดียว
  • อยากเก็บ failure หลายจุดในรอบเดียว

ไม่ค่อยเหมาะเมื่อ:

  • ขั้นต่อไปของ test พึ่งพาค่าที่ assertion ก่อนหน้านี้ยืนยันอยู่
8 · Custom message ช่วยให้ debug เร็วขึ้น
await expect(page.getByRole('button', { name: 'Checkout' }))
  .toBeEnabled({ message: 'Checkout button should be enabled after adding items' });

message ที่ดีควรบอก business expectation ไม่ใช่แค่บอกชื่อ element ซ้ำ

Practice

แบบฝึก: เลือก 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 ทั้งเสถียรและสื่อความหมายชัดขึ้นพร้อมกัน