Automation Frameworks
Automation Pyramid
เหมือน Test Pyramid แต่มองจากมุม "ควร automate อะไรเป็น สัดส่วนเท่าไหร่": automate unit/API เยอะที่สุด (เร็ว, ถูก, มั่นคง), automate E2E ผ่าน UI น้อยที่สุด (ช้า, แพง, เปราะบาง/flaky ง่ายสุด) เพราะ UI เปลี่ยนบ่อยและขึ้นกับหลายปัจจัยที่ควบคุม ไม่ได้ (network, rendering timing, third-party widget)
"Automate ชั้นล่างให้เต็ม ชั้นบนให้น้อยแต่สำคัญสุด" — UI automation แพงสุดทั้งตอนเขียนและตอน maintain (ทุกครั้งที่ UI เปลี่ยน test script ก็มีสิทธิ์พังตาม) ให้เก็บไว้ทดสอบเฉพาะ critical journey ที่คุ้มกับต้นทุนจริงๆ
ทีมจำนวนมากเข้าใจผิดว่า "automation coverage สูง = คุณภาพดี" แล้วไล่ automate E2E test ทุก scenario รวมถึง edge case เล็กๆ น้อยๆ ที่ API test ระดับล่างครอบได้อยู่แล้ว ผลคือ E2E suite บวม ขึ้นจนรันนานหลายชั่วโมงและ flaky สูงจนทีมเริ่มไม่เชื่อผลเทสต์ (เหมือนปัญหา "crying wolf" — fail บ่อยจนคนเลิกสนใจ fail จริง) หลักที่ถูกต้องคือ "ทุก edge case ที่ API test ครอบได้แล้ว ไม่ จำเป็นต้องมี E2E ซ้ำอีก" — E2E ควรทดสอบแค่ "เส้นทางที่ component ต่อกันจริงใช้งานได้" ไม่ใช่ ไล่ทดสอบ logic เดิมซ้ำที่ชั้นล่างทดสอบไปแล้ว
เลือก Framework: Selenium vs Playwright vs Cypress
| คุณสมบัติ | Selenium | Cypress | Playwright |
|---|---|---|---|
| Browser support | หลากหลายที่สุด (รวม browser เก่า) ผ่าน WebDriver | Chromium-based เป็นหลัก | Chromium, Firefox, WebKit |
| สถาปัตยกรรม | สั่งงานผ่าน WebDriver protocol (นอก process ของ browser) | รันอยู่ "ใน" browser เดียวกับแอป | ใช้ browser automation protocol โดยตรง (CDP-based) |
| ความเร็ว/เสถียรภาพ | ช้ากว่า, setup wait ต้องจัดการเอง (เสี่ยง flaky) | เร็ว, มี auto-wait ในตัว | เร็ว, มี auto-wait + network interception แข็งแรง |
| Multi-tab/iframe | รองรับแต่ยุ่งยาก | จำกัดกว่า (ออกแบบมาเพื่อ single-tab) | รองรับดี (multi-tab, multi-context) |
| ภาษาที่ใช้เขียน | หลายภาษา (Java, Python, C#, JS, …) | JS/TS เท่านั้น | JS/TS, Python, Java, C# |
"Selenium = ครอบจักรวาลแต่ต้องดูแลเยอะ, Cypress = ง่ายและเร็วแต่มีข้อจำกัด, Playwright = สมดุลที่สุดสำหรับงานใหม่ตอนนี้" — เลือกตามบริบททีม ไม่ใช่เลือกตามความฮิต
ความต่างที่ลึกกว่าตารางฟีเจอร์คือ สถาปัตยกรรมการสื่อสารกับ browser — Selenium คุยผ่าน WebDriver protocol ซึ่งเป็น HTTP call ไปมาระหว่าง test process กับ browser driver (มี overhead และ "window ของความไม่แน่นอน" ทุกครั้งที่ assert สภาพ DOM ทำให้ต้องเขียน explicit wait เองเพื่อ กัน flaky) ส่วน Cypress รันโค้ดเทสต์ อยู่ใน browser เดียวกับที่แอปรัน ทำให้มัน "รู้" สถานะ DOM/network แบบ real-time และทำ auto-retry/auto-wait โดยไม่ต้องเขียน wait เอง แต่ข้อเสียคือ ข้อจำกัดเรื่อง multi-tab/origin ที่ตามมาจากสถาปัตยกรรมเดียวกันนี้ (เพราะมันผูกกับ context ของ tab เดียว) Playwright แก้จุดนี้ด้วยการคุยผ่าน browser automation protocol โดยตรง (ไม่ผ่าน WebDriver) ทำให้ได้ทั้งความเร็ว/auto-wait แบบ Cypress และยังรองรับ multi-tab/multi-context แบบ Selenium ไปด้วย — เป็นเหตุผลเชิงเทคนิคจริงที่ทีมใหม่ส่วนใหญ่ตอนนี้เลือก Playwright ไม่ใช่แค่ เพราะ "เทรนด์ใหม่กว่า"
Page Object Model (POM) & Locator Strategy
Page Object Model คือ pattern ที่ห่อ element locator และ action ของแต่ละหน้าไว้ใน class แยก ทำให้เทสต์ไม่ผูกกับ locator ตรงๆ — ถ้า UI เปลี่ยน แก้ที่ page object ที่เดียว ไม่ต้องไล่แก้ ทุก test file ที่อ้างถึงหน้านั้น
// ไม่ดี: locator กระจายอยู่ในทุก test case
test('login success', async ({ page }) => {
await page.fill('#email-input', 'user@test.com');
await page.fill('#password-input', 'secret');
await page.click('button[type=submit]');
});
// ดี: encapsulate ผ่าน Page Object
class LoginPage {
constructor(private page: Page) {}
async login(email: string, password: string) {
await this.page.fill('#email-input', email);
await this.page.fill('#password-input', password);
await this.page.click('button[type=submit]');
}
}
test('login success', async ({ page }) => {
await new LoginPage(page).login('user@test.com', 'secret');
});
Locator strategy ที่ดี เรียงความทนทาน (ทนต่อการเปลี่ยน UI) จากมากไปน้อย: data-testid ที่
ตั้งขึ้นมาเพื่อเทสต์โดยเฉพาะ > role/label (accessibility-based) > CSS class ที่มีความหมายคงที่
XPath/CSS selector ที่ผูกกับโครงสร้าง DOM ลึกๆ (เปราะบางที่สุด เพราะเปลี่ยนตาม layout ง่าย)
"POM คือการ refactor โค้ดเทสต์แบบเดียวกับที่ refactor โค้ด production" — DRY (Don't Repeat Yourself) ใช้ได้กับโค้ดเทสต์เหมือนกัน locator ที่ซ้ำในหลายไฟล์คือ technical debt ที่รอวันพัง พร้อมกันทีเดียวตอน UI เปลี่ยน
การใช้ data-testid แม้ทนทานสุด แต่มีข้อเสียที่มักถูกมองข้าม: มันทดสอบแค่ "element นี้มีอยู่"
ไม่ได้พิสูจน์ว่า element นั้น accessible จริงสำหรับผู้ใช้ที่ใช้ screen reader หรือ keyboard
navigation ทีม production-grade ที่ใส่ใจ accessibility จะเลือกใช้ role/label-based
locator (เช่น getByRole('button', { name: 'Submit' }) ใน Playwright) เป็นหลักเมื่อทำได้
เพราะมันทดสอบสองอย่างพร้อมกันในการเขียนโค้ดเทสต์ครั้งเดียว: (1) ฟังก์ชันทำงานถูก และ (2) element
นั้น expose accessibility tree ถูกต้องจริง (ซึ่งถ้า dev ลืมใส่ aria-label หรือใช้ <div>
แทน <button> เทสต์แบบ role-based จะ fail ทันที ในขณะที่เทสต์แบบ data-testid จะผ่านปกติ
ทั้งที่ accessibility จริงพังอยู่) — นี่คือเหตุผลที่ Playwright/Testing Library ผลักดันให้ใช้
role-based locator เป็น default แทน test-id มาตลอดหลังๆ
The automation pyramid mirrors the test pyramid — automate most at the unit/API layer, least at
the brittle, slow UI/E2E layer; duplicating API-level edge cases in E2E bloats suites and causes
flakiness that erodes trust in results. Selenium talks to browsers over the WebDriver protocol
(more overhead, needs manual waits); Cypress runs inside the browser for built-in auto-waiting
but is limited to single-tab contexts; Playwright uses a direct automation protocol, combining
Cypress's speed/auto-wait with Selenium's multi-tab/multi-context support. Page Object Model
centralizes locators so UI changes require a single fix point. Locator strategy should prefer
role/label-based locators over data-testid where practical, since they double as an
accessibility check — a missing aria-label fails a role-based locator but passes a data-testid
one even though real accessibility is broken.