Playwright Locator Strategies
locator คือหัวใจของความเสถียรใน UI automation ถ้าเลือก locator ดี test จะอ่านง่ายและพังยาก แต่ถ้าเลือกผิด ตั้งแต่วันแรก suite จะเปราะตามคุณไปอีกนาน
จบบทนี้แล้วคุณจะ...
- เข้าใจลำดับความน่าใช้ของ locator แต่ละแบบใน Playwright
- เลือก locator ให้เหมาะกับสถานการณ์จริงได้ดีขึ้น
- ใช้ chaining และ filtering เพื่อลดความกำกวมของ element
- เห็นชัดขึ้นว่าทำไม CSS/XPath ควรเป็นทางเลือกท้าย ๆ
- เขียน locator ที่สะท้อนสิ่งที่ผู้ใช้เห็น ไม่ใช่แค่สิ่งที่ HTML บังเอิญมี
test ที่พังบ่อยจำนวนมาก ไม่ได้พังเพราะ assertion ผิด แต่พังตั้งแต่หา element ไม่เจอ
locator คือวิธีที่ Playwright ใช้หา element บนหน้าเว็บ คุณอาจคิดว่ามันเป็นแค่ selector แต่จริง ๆ แล้วมันสะท้อนแนวคิดว่าเราอยากอ้างอิง element แบบไหน:
- ตามสิ่งที่ผู้ใช้เห็น
- ตาม semantic role
- ตาม label ของฟอร์ม
- หรือตาม technical hook ที่ทีมเตรียมไว้
Playwright แนะนำลำดับการเลือก locator แบบนี้:
- getByRole
- getByText
- getByLabel
- getByPlaceholder
- getByTestId
- locator() แบบ CSS/XPath
เพราะมันยึดกับความหมายของ 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 นั้น “คืออะไร”
เหมาะกับข้อความที่ผู้ใช้ควรเห็น ไม่ใช่ทุก element บนหน้า
page.getByText('Welcome to our store');
page.getByText('Welcome', { exact: true });
page.getByText(/welcome/i);
เหมาะกับ:
- flash message
- heading
- paragraph หรือ label ที่มีข้อความเฉพาะ
สิ่งที่ควรระวัง:
- ถ้าข้อความมีหลายจุดในหน้า อาจกำกวม
- ถ้าระบบรองรับหลายภาษา locator นี้อาจเปราะ
สองตัวนี้ช่วยให้ form automation อ่านง่ายมาก
page.getByLabel('Email Address');
page.getByLabel('Username');
page.getByPlaceholder('Search products...');
page.getByPlaceholder('Enter your email');
คำแนะนำ:
- ถ้ามี label ให้ใช้
getByLabel()ก่อน placeholderเป็นตัวช่วยที่ดี แต่ไม่ควรแทน label ในระบบที่ออกแบบดี
ดีมากเมื่อข้อความหรือ DOM structure ไม่เสถียรพอ
page.getByTestId('product-card');
page.getByTestId('submit-order');
เหมาะกับ:
- component ที่มีซ้ำหลายตัว
- dynamic text
- element ที่ business logic ต้องอ้างอิงบ่อย
เพราะมันมักอิงกับโครงสร้างที่เปลี่ยนง่าย
page.locator('.product-card');
page.locator('#submit-btn');
page.locator('div.container > button.primary');
page.locator('xpath=//button[@type=\"submit\"]');
ใช้เมื่อ:
- ระบบเก่ามากและไม่มี hook ที่ดีกว่า
- คุณต้องจับโครงสร้างเฉพาะจริง ๆ
- กำลัง refactor จาก selector แบบเก่าไปสู่ 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
แบบฝึก: เลือก 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 แม่นขึ้นมาก