Test Organization Patterns
จบบทนี้แล้วคุณจะ...
- จัดกลุ่ม test ด้วย
test.describeแบบ nesting - เลือกระหว่าง grouping by feature กับ by page
- ใช้ test tagging (@smoke, @regression) เพื่อ filter test
- ตั้งค่า parallel execution groups
1 · ทำไม Test Organization สำคัญ?
เมื่อ test suite โตจาก 10 เป็น 200+ tests:
- หา test ที่ต้องการไม่เจอ
- ไม่รู้ว่า test ไหนต้องรันก่อน deploy
- CI ช้าเพราะรันทุก test ทุกครั้ง
- test ที่ fail ไม่รู้ว่าเกี่ยวกับ feature ไหน
การจัดระเบียบดี = ทีมทำงานเร็วขึ้น
2 · test.describe — จัดกลุ่มอย่างเป็นระบบ
test.describe('Authentication', () => {
test.describe('Login', () => {
test('successful login with valid credentials', async ({ page }) => {
// ...
});
test('shows error with invalid password', async ({ page }) => {
// ...
});
test('redirects to dashboard after login', async ({ page }) => {
// ...
});
});
test.describe('Logout', () => {
test('clears session on logout', async ({ page }) => {
// ...
});
test('redirects to login page', async ({ page }) => {
// ...
});
});
});
ผลลัพธ์ใน report:
Authentication > Login > successful login with valid credentials
Authentication > Login > shows error with invalid password
Authentication > Logout > clears session on logout
3 · Grouping: By Feature vs By Page
By Feature (แนะนำ)
tests/
├── auth/
│ ├── login.spec.ts
│ ├── logout.spec.ts
│ └── forgot-password.spec.ts
├── products/
│ ├── product-list.spec.ts
│ ├── product-detail.spec.ts
│ └── product-search.spec.ts
├── cart/
│ ├── add-to-cart.spec.ts
│ ├── update-quantity.spec.ts
│ └── checkout.spec.ts
└── admin/
├── user-management.spec.ts
└── reports.spec.ts
By Page
tests/
├── login-page.spec.ts
├── dashboard-page.spec.ts
├── products-page.spec.ts
├── cart-page.spec.ts
└── admin-page.spec.ts
| By Feature | By Page | |
|---|---|---|
| ข้อดี | ตรงกับ user journey, หา test ง่าย | เรียบง่าย, 1 page = 1 file |
| ข้อเสีย | file อาจเยอะ | test ไม่สะท้อน business flow |
| เหมาะกับ | ทีมใหญ่, feature หลายตัว | โปรเจกต์เล็ก, หน้าไม่เยอะ |
4 · Shared Setup ด้วย beforeEach
test.describe('Dashboard - Authenticated User', () => {
test.beforeEach(async ({ page }) => {
// login ก่อนทุก test
await page.goto('/login');
await page.locator('#email').fill('[email protected]');
await page.locator('#password').fill('secret123');
await page.locator('button[type="submit"]').click();
await page.waitForURL('/dashboard');
});
test('displays welcome message', async ({ page }) => {
await expect(page.locator('.welcome')).toBeVisible();
});
test('shows recent orders', async ({ page }) => {
await expect(page.locator('.recent-orders')).toBeVisible();
});
});
5 · Test Tagging — @smoke, @regression
Playwright ใช้ tag ใน test title หรือ test.describe.configure:
// วิธีที่ 1: ใส่ tag ใน title
test('login with valid credentials @smoke', async ({ page }) => {
// ...
});
test('login with expired token @regression', async ({ page }) => {
// ...
});
// วิธีที่ 2: ใช้ tag option (Playwright 1.42+)
test('checkout flow', { tag: ['@smoke', '@critical'] }, async ({ page }) => {
// ...
});
รัน test ตาม tag:
# รันเฉพาะ @smoke
npx playwright test --grep @smoke
# รันทุกอย่างยกเว้น @slow
npx playwright test --grep-invert @slow
# รัน @smoke และ @critical
npx playwright test --grep "@smoke|@critical"
แนะนำ tags:
@smoke— test หลักที่ต้องผ่านก่อน deploy (5-10 ตัว)@regression— ครอบคลุมกว้าง รันรอบคืน@critical— business-critical flow เช่น payment@slow— test ที่ใช้เวลานาน ไม่ต้องรันทุก PR
6 · Parallel Execution
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true, // ทุก test รัน parallel
workers: 4, // ใช้ 4 workers
projects: [
{
name: 'smoke',
grep: /@smoke/,
fullyParallel: true,
},
{
name: 'regression',
grep: /@regression/,
fullyParallel: false, // sequential สำหรับ tests ที่มี dependencies
}
]
});
ข้อควรระวัง parallel:
- test แต่ละตัวต้อง independent — ไม่พึ่ง state ของ test อื่น
- ใช้
test.describe.serial()สำหรับ test ที่ต้องรันตามลำดับ
test.describe.serial('Order Flow', () => {
test('step 1: add to cart', async ({ page }) => { /* ... */ });
test('step 2: checkout', async ({ page }) => { /* ... */ });
test('step 3: confirm payment', async ({ page }) => { /* ... */ });
});
แบบฝึกหัด: จัดระเบียบ Test Files
โจทย์: คุณได้รับ test files ที่วางระเรียงอย่างไม่มีระบบ:
tests/
├── test1.spec.ts (login tests)
├── test2.spec.ts (product search + product detail)
├── test3.spec.ts (add to cart + checkout + payment)
├── test4.spec.ts (admin user management)
├── test5.spec.ts (forgot password + reset password)
ให้ทำ:
- เสนอโครงสร้าง folder ใหม่แบบ by feature
- กำหนด tags ที่เหมาะสม
- ระบุว่า tests ไหนควรเป็น
@smoke
ดูคำตอบแนะนำ
tests/
├── auth/
│ ├── login.spec.ts @smoke (login success)
│ ├── forgot-password.spec.ts
│ └── reset-password.spec.ts
├── products/
│ ├── product-search.spec.ts @smoke (search works)
│ └── product-detail.spec.ts
├── cart/
│ ├── add-to-cart.spec.ts @smoke (add item works)
│ └── checkout.spec.ts @smoke @critical (payment flow)
└── admin/
└── user-management.spec.ts @regression
@smoke tests (รันทุก PR):
- login success — เพราะทุก feature ต้องใช้ login
- product search — เพราะเป็น primary user journey
- add to cart — เพราะเกี่ยวกับ revenue
- checkout/payment — เพราะเป็น business-critical
สิ่งที่ต้องจำ
test.describeจัดกลุ่ม test ให้อ่านง่ายและ report ชัดเจน- Group by feature เหมาะกับทีมใหญ่ — ตรงกับ business flow
- Tags (@smoke, @regression) ช่วย filter test ตาม context — ไม่ต้องรันทุกตัวทุกครั้ง
fullyParallelเร็วขึ้น แต่ test ต้อง independent- ใช้
test.describe.serial()สำหรับ tests ที่ต้องรันตามลำดับเท่านั้น