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

Playwright Locator Strategies

locator คือหัวใจของความเสถียรใน UI automation ถ้าเลือก locator ดี test จะอ่านง่ายและพังยาก แต่ถ้าเลือกผิด ตั้งแต่วันแรก suite จะเปราะตามคุณไปอีกนาน

เวลาเรียน: 20–24 นาที ต้องรู้มาก่อน: Module 01 - เขียน test เบื้องต้นกับ Playwright ได้แล้ว รูปแบบ: อ่าน + ตัวอย่างหลายแบบ + แบบฝึกเลือก locator

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

  • เข้าใจลำดับความน่าใช้ของ locator แต่ละแบบใน Playwright
  • เลือก locator ให้เหมาะกับสถานการณ์จริงได้ดีขึ้น
  • ใช้ chaining และ filtering เพื่อลดความกำกวมของ element
  • เห็นชัดขึ้นว่าทำไม CSS/XPath ควรเป็นทางเลือกท้าย ๆ
  • เขียน locator ที่สะท้อนสิ่งที่ผู้ใช้เห็น ไม่ใช่แค่สิ่งที่ HTML บังเอิญมี
1 · Locator สำคัญกว่าที่คิด

test ที่พังบ่อยจำนวนมาก ไม่ได้พังเพราะ assertion ผิด แต่พังตั้งแต่หา element ไม่เจอ

locator คือวิธีที่ Playwright ใช้หา element บนหน้าเว็บ คุณอาจคิดว่ามันเป็นแค่ selector แต่จริง ๆ แล้วมันสะท้อนแนวคิดว่าเราอยากอ้างอิง element แบบไหน:

  • ตามสิ่งที่ผู้ใช้เห็น
  • ตาม semantic role
  • ตาม label ของฟอร์ม
  • หรือตาม technical hook ที่ทีมเตรียมไว้

Playwright แนะนำลำดับการเลือก locator แบบนี้:

  1. getByRole
  2. getByText
  3. getByLabel
  4. getByPlaceholder
  5. getByTestId
  6. locator() แบบ CSS/XPath
2 · `getByRole()` เป็น default choice ที่ดีที่สุดในหลายกรณี

เพราะมันยึดกับความหมายของ element มากกว่าโครงสร้างหน้าตา

page.getByRole('button', { name: 'Submit' });
page.getByRole('link', { name: 'Home' });
page.getByRole('heading', { name: 'Welcome' });
page.getByRole('checkbox', { name: 'Accept terms' });
page.getByRole('textbox', { name: 'Email' });

ข้อดีหลัก:

  • ใกล้กับวิธีที่ผู้ใช้และ screen reader มองหน้า
  • มักเสถียรกว่า CSS classes ที่เปลี่ยนตามดีไซน์
  • อ่านแล้วรู้เลยว่า element นั้น “คืออะไร”
3 · `getByText()` ใช้เมื่อข้อความคือสิ่งสำคัญจริง

เหมาะกับข้อความที่ผู้ใช้ควรเห็น ไม่ใช่ทุก element บนหน้า

page.getByText('Welcome to our store');
page.getByText('Welcome', { exact: true });
page.getByText(/welcome/i);

เหมาะกับ:

  • flash message
  • heading
  • paragraph หรือ label ที่มีข้อความเฉพาะ

สิ่งที่ควรระวัง:

  • ถ้าข้อความมีหลายจุดในหน้า อาจกำกวม
  • ถ้าระบบรองรับหลายภาษา locator นี้อาจเปราะ
4 · `getByLabel()` และ `getByPlaceholder()`

สองตัวนี้ช่วยให้ form automation อ่านง่ายมาก

page.getByLabel('Email Address');
page.getByLabel('Username');
page.getByPlaceholder('Search products...');
page.getByPlaceholder('Enter your email');

คำแนะนำ:

  • ถ้ามี label ให้ใช้ getByLabel() ก่อน
  • placeholder เป็นตัวช่วยที่ดี แต่ไม่ควรแทน label ในระบบที่ออกแบบดี
5 · `getByTestId()` คือ technical hook ที่ทีมควบคุมได้

ดีมากเมื่อข้อความหรือ DOM structure ไม่เสถียรพอ

page.getByTestId('product-card');
page.getByTestId('submit-order');

เหมาะกับ:

  • component ที่มีซ้ำหลายตัว
  • dynamic text
  • element ที่ business logic ต้องอ้างอิงบ่อย
6 · CSS/XPath ยังใช้ได้ แต่ควรเป็นทางเลือกท้าย

เพราะมันมักอิงกับโครงสร้างที่เปลี่ยนง่าย

page.locator('.product-card');
page.locator('#submit-btn');
page.locator('div.container > button.primary');
page.locator('xpath=//button[@type=\"submit\"]');

ใช้เมื่อ:

  • ระบบเก่ามากและไม่มี hook ที่ดีกว่า
  • คุณต้องจับโครงสร้างเฉพาะจริง ๆ
  • กำลัง refactor จาก selector แบบเก่าไปสู่ locator ที่เสถียรกว่า
7 · Chaining และ filtering ช่วยให้ locator แม่นขึ้น

โดยเฉพาะเวลาหน้ามี element ชื่อคล้ายกันหลายตัว

page.getByRole('dialog').getByRole('button', { name: 'Confirm' });

page.getByRole('listitem').filter({ hasText: 'iPhone' });

page.getByRole('listitem').filter({
  has: page.getByRole('button', { name: 'Add to cart' }),
});

page.getByRole('listitem').first();
page.getByRole('listitem').last();

หลักคิด:

  • เริ่มจาก scope ที่แคบลงก่อน
  • เลือก locator ที่บอก “ของชิ้นไหน” ให้ชัด
  • อย่ารีบใช้ .nth() ถ้ายังหาวิธีอธิบาย element ได้ด้วยข้อความหรือ role
Practice

แบบฝึก: เลือก locator ที่เสถียรกว่า

HTML 1

<button>Add to Cart</button>
ดูคำตอบแนะนำ

page.getByRole('button', { name: 'Add to Cart' }) เพราะ role และชื่อชัดเจนที่สุด

HTML 2

<label for="email">Email Address</label>
<input id="email" type="email" placeholder="[email protected]" />
ดูคำตอบแนะนำ

page.getByLabel('Email Address') เพราะถ้ามี label อยู่แล้ว locator นี้สื่อความหมายดีกว่า placeholder

HTML 3

<div data-testid="error-message">Invalid credentials</div>
ดูคำตอบแนะนำ

ใช้ page.getByTestId('error-message') จะเสถียรกว่า ถ้าข้อความมีโอกาสเปลี่ยนตามภาษา ส่วน getByText() ก็ใช้ได้ถ้าข้อความนิ่งและนั่นคือสิ่งที่อยากพิสูจน์

HTML 4

<input placeholder="Search..." />
ดูคำตอบแนะนำ

page.getByPlaceholder('Search...') เพราะไม่มี label แต่มี placeholder ที่ชัดเจน

HTML 5

<nav>
  <a href="/home">Home</a>
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
</nav>
ดูคำตอบแนะนำ

page.getByRole('link', { name: 'About' }) เพราะเป็นลิงก์ที่ role และชื่อชัด อ่านแล้วรู้ความหมายทันที

สิ่งที่ต้องเอาติดตัวไปจากบทนี้

  • ลำดับที่มักปลอดภัยคือ getByRole ก่อน แล้วค่อยพิจารณา getByText, getByLabel, getByPlaceholder, getByTestId
  • locator ที่ดีควรสะท้อนสิ่งที่ผู้ใช้เห็นหรือสิ่งที่ทีมตั้งใจให้ทดสอบ
  • data-testid เป็นทางเลือกที่ดีเมื่อข้อความหรือ DOM structure ไม่เสถียร
  • CSS/XPath ใช้ได้แต่ควรเป็นตัวเลือกท้าย เพราะเปราะและอ่านยากกว่า
  • การใช้ chaining และ filtering ช่วยลด ambiguity และทำให้ test แม่นขึ้นมาก