การทดสอบคอมโพเนนต์
ระบบการออกแบบของเราใช้ Playwright Component Testing เพื่อให้แน่ใจว่าพฤติกรรมคอมโพเนนต์แข็งแกร่งและเชื่อถือได้ คู่มือนี้ครอบคลุมแนวทางการทดสอบของเรา ซึ่งขับเคลื่อนโดย Playwright MCP (Model Context Protocol) สำหรับการสร้างและดำเนินการทดสอบที่ชาญฉลาด
ภาพรวม
Playwright Component Testing มอบ:
- การดำเนินการที่รวดเร็ว ด้วยการเรนเดอร์เบราว์เซอร์จริง
- ความครอบคลุมที่ครอบคลุม ของการโต้ตอบของผู้ใช้
- การทดสอบการเข้าถึง ในตัว
- ความสามารถในการทดสอบ visual regression
- ความเข้ากันได้ข้ามเบราว์เซอร์
สถาปัตยกรรมการทดสอบ
โครงสร้างการทดสอบ
tests/
├── components/
│ ├── 1-component-test-initial-prompt.md # prompt การสร้างทดสอบ AI
│ ├── Button.spec.ts # ทดสอบคอมโพเนนต์
│ ├── Modal.spec.ts
│ └── ...การกำหนดค่า
การทดสอบคอมโพเนนต์ Playwright ของเราได้รับการกำหนดค่าใน playwright-ct.config.ts:
export default defineConfig({
testDir: './tests/components',
fullyParallel: true,
reporter: 'html',
use: {
trace: 'on-first-retry',
ctTemplateDir: 'playwright',
ctPort: 3100,
ctViteConfig: {
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
},
},
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});การสร้างทดสอบที่ขับเคลื่อนด้วย AI
การผสานรวม Playwright MCP
เราใช้ Playwright MCP (Model Context Protocol) เพื่อสร้างการทดสอบคอมโพเนนต์ที่ครอบคลุม วิธีการที่ขับเคลื่อนด้วย AI นี้ทำให้แน่ใจได้ว่า:
- ความครอบคลุมที่สมบูรณ์ ของฟีเจอร์คอมโพเนนต์ทั้งหมด
- รูปแบบการทดสอบที่สอดคล้องกัน ในคอมโพเนนต์ทั้งหมด
- การสร้างกรณีทดสอบที่ชาญฉลาด ตามการวิเคราะห์คอมโพเนนต์
- แนวทางการทดสอบที่เน้นการเข้าถึงเป็นหลัก
- การสร้างโค้ดที่เป็นไปตาม lint ตามมาตรฐานโปรเจกต์
ระบบ Initial Prompt
การสร้างทดสอบของเราใช้ initial prompt ที่อยู่ใน tests/components/1-component-test-initial-prompt.md prompt นี้ให้:
- ข้อกำหนดการทดสอบที่ครอบคลุม
- รูปแบบเฉพาะ Vue 3
- แนวทางการทดสอบการเข้าถึง
- มาตรฐาน linting และคุณภาพโค้ด
- กลยุทธ์การจัดการข้อผิดพลาด
- แนวทางปฏิบัติและแบบแผนที่ดีที่สุด
การใช้เครื่องมือสร้างทดสอบ AI
ในการสร้างการทดสอบสำหรับคอมโพเนนต์ใหม่โดยใช้ระบบ MCP:
ข้อกำหนดโมเดล
ใช้ Claude Sonnet 4+ เป็นโมเดล AI ของคุณเมื่อสร้างการทดสอบคอมโพเนนต์ สิ่งนี้ทำให้แน่ใจได้ว่าคุณภาพการทดสอบที่เหมาะสม ความครอบคลุมที่ครอบคลุม และการปฏิบัติตามรูปแบบการทดสอบและ Vue 3 ที่ดีที่สุดของเรา
# ให้ข้อมูลคอมโพเนนต์ในรูปแบบนี้:
<<<COMPONENT
[ซอร์สโค้ด SFC ของ Vue ที่นี่]
COMPONENT>>>
<<<STRUCTURE
[โครงสร้างโฟลเดอร์จาก root src/]
STRUCTURE>>>
<<<RELATED
[ไม่บังคับ: composables ที่เกี่ยวข้อง ประเภท หรือคอมโพเนนต์]
RELATED>>>AI จะวิเคราะห์คอมโพเนนต์ของคุณและสร้างการทดสอบที่ครอบคลุมซึ่งครอบคลุม:
- การเรนเดอร์และการ mount
- การตรวจสอบ props และการรวม
- การปล่อย events
- ฟังก์ชันการทำงานของ slot
- ข้อกำหนดการเข้าถึง
- การจัดการข้อผิดพลาด
- การโต้ตอบของผู้ใช้
- คุณภาพโค้ดและการปฏิบัติตาม lint
ข้อกำหนดความครอบคลุมการทดสอบ
พื้นที่การทดสอบหลัก
1. การทดสอบการเรนเดอร์
test('ควรเรนเดอร์คอมโพเนนต์ด้วย props เริ่มต้น', async ({ mount }) => {
const component = await mount(Button);
await expect(component).toBeVisible();
});2. การทดสอบ Props
test('ควรใช้ variants ขนาดอย่างถูกต้อง', async ({ mount }) => {
const component = await mount(Button, { props: { size: 'large' } });
await expect(component).toHaveClass(/spr-size-large/);
});3. การทดสอบ Events
test('ควรปล่อย event click ด้วย payload ที่ถูกต้อง', async ({ mount }) => {
let clickEvent: any;
const component = await mount(Button, {
on: {
click: (event) => {
clickEvent = event;
},
},
});
await component.click();
expect(clickEvent).toBeTruthy();
});4. การทดสอบการเข้าถึง
test('ควรมี attributes ARIA ที่ถูกต้อง', async ({ mount }) => {
const component = await mount(Button, { props: { disabled: true } });
await expect(component).toHaveAttribute('aria-disabled', 'true');
});
test('ควรนำทางได้ด้วย keyboard', async ({ mount, page }) => {
await mount(Button);
await page.keyboard.press('Tab');
await expect(page.getByRole('button')).toBeFocused();
});5. การทดสอบ Slot
test('ควรเรนเดอร์เนื้อหา slot อย่างถูกต้อง', async ({ mount }) => {
const component = await mount(Button, {
slots: { default: 'ข้อความปุ่มแบบกำหนดเอง' },
});
await expect(component).toContainText('ข้อความปุ่มแบบกำหนดเอง');
});สถานการณ์การทดสอบขั้นสูง
การเรนเดอร์แบบมีเงื่อนไข
test('ควรเรนเดอร์องค์ประกอบแบบมีเงื่อนไขตาม props', async ({ mount }) => {
const component = await mount(Modal, { props: { showHeader: false } });
await expect(component.locator('.modal-header')).not.toBeVisible();
});การผสานรวมฟอร์ม
test('ควรผสานรวมกับการตรวจสอบฟอร์ม', async ({ mount }) => {
const component = await mount(Input, {
props: { required: true, value: '' },
});
await component.blur();
await expect(component).toHaveAttribute('aria-invalid', 'true');
});Theme และ Styling
test('ควรใช้ variants theme', async ({ mount }) => {
const component = await mount(Button, {
props: { variant: 'primary' },
});
await expect(component).toHaveClass(/spr-variant-primary/);
});แนวทางปฏิบัติที่ดีที่สุด
คุณภาพโค้ดและ Linting
ก่อนรันการทดสอบ ตรวจสอบให้แน่ใจว่าไฟล์ทดสอบของคุณตรงตามมาตรฐานคุณภาพโค้ด:
# ตรวจสอบปัญหา lint ในไฟล์ทดสอบ
npm run lintข้อกำหนด linting ที่สำคัญสำหรับไฟล์ทดสอบ:
- การจัดรูปแบบโค้ดที่สอดคล้องกันและสไตล์
- การพิมพ์ TypeScript ที่เหมาะสม
- การปฏิบัติตามกฎ ESLint
- การจัดระเบียบคำสั่ง import
- แบบแผนการตั้งชื่อที่สอดคล้องกัน
กลยุทธ์ Selector (ลำดับความสำคัญ)
- selectors แบบ role-based (แนะนำ):
page.getByRole('button', { name: 'ส่ง' });
page.getByRole('textbox', { name: 'อีเมล' });- selectors เนื้อหาข้อความ:
page.getByText('คลิกฉัน');
page.getByLabel('ที่อยู่ อีเมล');- test IDs (เมื่อจำเป็น):
page.getByTestId('submit-btn');การเขียนการทดสอบที่บำรุงรักษาได้
ใช้ชื่อการทดสอบที่สื่อความหมาย
// ✅ ดี
test('ควรปิดใช้งานปุ่มและป้องกันการคลิกเมื่อ prop disabled เป็น true');
// ❌ ไม่ดี
test('ทดสอบ disabled');จัดกลุ่มการทดสอบที่เกี่ยวข้อง
test.describe('คอมโพเนนต์ Button', () => {
test.describe('Props', () => {
test('ควรเรนเดอร์ด้วยขนาดเริ่มต้น');
test('ควรใช้ variants ขนาดแบบกำหนดเอง');
});
test.describe('Events', () => {
test('ควรปล่อย events click');
test('ควรป้องกัน events เมื่อปิดใช้งาน');
});
});หลีกเลี่ยงปัญหา timing
// ✅ ดี - รอเงื่อนไขเฉพาะ
await expect(modal).toBeVisible();
// ❌ ไม่ดี - timeout แบบกำหนดเอง
await page.waitForTimeout(500);การรันการทดสอบ
การพัฒนาในเครื่อง
# ตรวจสอบปัญหา lint ในไฟล์ทดสอบ (แนะนำก่อนรันการทดสอบ)
npm run lint
# รันการทดสอบคอมโพเนนต์ทั้งหมด
npm run test:components
# รันการทดสอบคอมโพเนนต์เฉพาะโดยใช้ grep filter
npm run test:component -- --grep "DatePicker"
# รันไฟล์ทดสอบเฉพาะ (เส้นทางเต็ม)
npx playwright test tests/components/Button.spec.ts --config=playwright-ct.config.ts
# รันด้วยโหมด UI สำหรับการดีบัก
npx playwright test --ui --config=playwright-ct.config.ts
# สร้างรายงานการทดสอบ
npx playwright show-reportการดีบักการทดสอบ
การดีบักภาพ
// เพิ่มในทดสอบสำหรับการดีบักภาพ
await page.pause();Trace Viewer
# รันทดสอบด้วย trace
npx playwright test --trace on
# ดู trace
npx playwright show-trace trace.zipScreenshots เมื่อล้มเหลว
test('ควรเรนเดอร์อย่างถูกต้อง', async ({ mount }, testInfo) => {
const component = await mount(Button);
// ถ่ายภาพหน้าจอเมื่อล้มเหลว
if (testInfo.retry > 0) {
await testInfo.attach('screenshot', {
body: await page.screenshot(),
contentType: 'image/png',
});
}
});ตัวอย่างการทดสอบคอมโพเนนต์
การทดสอบคอมโพเนนต์แบบง่าย
import { test, expect } from '@playwright/experimental-ct-vue';
import Button from '@/components/button/button.vue';
test.describe('คอมโพเนนต์ Button', () => {
test('ควรเรนเดอร์ด้วย props เริ่มต้น', async ({ mount }) => {
const component = await mount(Button);
await expect(component).toBeVisible();
await expect(component).toHaveClass(/spr-button/);
});
test('ควรจัดการ events click', async ({ mount }) => {
let clicked = false;
const component = await mount(Button, {
on: {
click: () => {
clicked = true;
},
},
});
await component.click();
expect(clicked).toBe(true);
});
});การทดสอบคอมโพเนนต์ที่ซับซ้อน
import { test, expect } from '@playwright/experimental-ct-vue';
import Modal from '@/components/modal/modal.vue';
test.describe('คอมโพเนนต์ Modal', () => {
test('ควรจัดการ focus อย่างถูกต้อง', async ({ mount, page }) => {
const component = await mount(Modal, {
props: {
modelValue: true,
title: 'โมดอลทดสอบ',
},
});
// ควร focus โมดอล
await expect(component).toBeFocused();
// ควร trap focus ภายในโมดอล
await page.keyboard.press('Tab');
const focusedElement = await page.evaluate(() => document.activeElement?.tagName);
expect(['BUTTON', 'INPUT', 'A']).toContain(focusedElement);
});
test('ควรปิดเมื่อกดปุ่ม Escape', async ({ mount, page }) => {
let modelValue = true;
await mount(Modal, {
props: {
modelValue,
'onUpdate:modelValue': (value: boolean) => {
modelValue = value;
},
},
});
await page.keyboard.press('Escape');
expect(modelValue).toBe(false);
});
});การทดสอบการเข้าถึง
Attributes ARIA
test('ควรมี attributes ARIA ที่ถูกต้อง', async ({ mount }) => {
const component = await mount(Button, {
props: { disabled: true, 'aria-label': 'ส่งฟอร์ม' },
});
await expect(component).toHaveAttribute('aria-disabled', 'true');
await expect(component).toHaveAttribute('aria-label', 'ส่งฟอร์ม');
});การนำทางด้วย Keyboard
test('ควรรองรับการนำทางด้วย keyboard', async ({ mount, page }) => {
await mount(Dropdown);
// เปิดด้วย Enter
await page.keyboard.press('Enter');
await expect(page.getByRole('listbox')).toBeVisible();
// นำทางด้วยลูกศร
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('option').first()).toBeFocused();
});การรองรับ Screen Reader
test('ควรให้การประกาศ screen reader', async ({ mount, page }) => {
const component = await mount(Snackbar, {
props: { message: 'สำเร็จ!', type: 'success' },
});
await expect(component).toHaveAttribute('role', 'alert');
await expect(component).toHaveAttribute('aria-live', 'polite');
});การผสานรวมกับ CI/CD
การทดสอบคอมโพเนนต์ของเรารันโดยอัตโนมัติใน pipelines CI/CD:
# ตัวอย่าง Azure Pipelines
- task: Node.js
inputs:
command: 'custom'
customCommand: 'npm run lint'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: Node.js
inputs:
command: 'custom'
customCommand: 'npm run test:components'
workingDirectory: '$(System.DefaultWorkingDirectory)'ทั้ง linting และการทดสอบต้องผ่านก่อนที่โค้ดจะถูกรวมเข้ากับ main branches
การขอความช่วยเหลือ
สำหรับความช่วยเหลือในการทดสอบคอมโพเนนต์:
- ตรวจสอบตัวอย่างการทดสอบที่มีอยู่ ใน
tests/components/ - ใช้เครื่องมือสร้างทดสอบ AI ด้วย initial prompt
- ตรวจสอบเอกสาร Playwright สำหรับรูปแบบขั้นสูง
- ถามทีม สำหรับกลยุทธ์การทดสอบเฉพาะคอมโพเนนต์
เคล็ดลับสำหรับมือโปร
ใช้ระบบ Playwright MCP เพื่อสร้างการทดสอบที่ครอบคลุมอย่างรวดเร็ว AI เข้าใจรูปแบบคอมโพเนนต์ของเราและจะสร้างการทดสอบที่ปฏิบัติตามแบบแผนของเราและครอบคลุมสถานการณ์ที่จำเป็นทั้งหมด
สำคัญ
รันการตรวจสอบ lint และการทดสอบในเครื่องเสมอก่อน commit การ lint ที่ล้มเหลวหรือการทดสอบจะบล็อกการ deploy CI/CD และป้องกันการรวม pull requests
# workflow ก่อน commit ที่แนะนำ
npm run lint # ตรวจสอบคุณภาพโค้ด
npm run test:components # รันการทดสอบคอมโพเนนต์ทั้งหมด
# หรือทดสอบคอมโพเนนต์เฉพาะระหว่างการพัฒนา
npm run test:component -- --grep "DatePicker"