Hallucination, False Positive และกับดักของ AI Tests
บทนี้พาเจาะลึก failure mode ของ AI ในงาน testing ตั้งแต่ hallucination ไปจนถึง test ที่ผ่านแต่ผิด พร้อม mental model ในการป้องกันที่ใช้ได้กับทุกเครื่องมือ
จบบทนี้แล้วคุณจะ...
- ระบุ failure mode หลักของ AI ในงาน testing ได้ครบ
- เข้าใจว่า "test ที่ผ่าน" ไม่เท่ากับ "test ที่ถูก" เมื่อ AI gen
- มี mental model ในการ skeptic AI output ที่ใช้ได้กับทุก tool
- รู้วิธีตั้ง guardrail เพื่อกัน AI failure mode
list ที่ครอบคลุมทุกเครื่องมือ
1. Hallucination: AI สร้างสิ่งที่ไม่มีจริง
- API endpoint ที่ไม่มี
- function / method ของ library ที่ไม่มี version นั้น
- HTML element ที่ไม่อยู่ในหน้าจริง
- requirement / spec ที่ไม่ได้เขียน
2. Plausible-but-wrong: ผลที่ดูถูกแต่ผิดหลักการ
- test ที่ assert สิ่งที่หาง่าย แทนสิ่งที่สำคัญ
- locator ที่ทำงานแต่จะพังเร็ว
- mock ที่ shape ใกล้เคียงแต่ไม่ตรง
3. Confident-but-uncertain: ตอบมั่นใจแต่ความรู้ผิด
- บอก "ใช้ได้แน่นอน" กับสิ่งที่ AI ไม่เคยเจอ
- ไม่บอก "ไม่รู้" หรือ "ขึ้นอยู่กับ context"
4. Context loss: หลุด context ระหว่างทาง
- session ยาว AI ลืม convention ทีมที่บอกตอนต้น
- prompt chain ลืม constraint ที่ตั้งไว้
5. Reward hacking: ทำให้ "test ผ่าน" แทน "verify behavior"
- AI เสนอลด assertion ลงเพื่อให้ผ่าน
- AI เพิ่ม
try/catchกลบ error - AI เปลี่ยน test ให้ test สิ่งที่ implementation ปัจจุบันทำได้
case ที่เกิดบ่อยใน Playwright workflow
Engineer ขอ AI gen test สำหรับหน้า settings:
write a Playwright test that opens the user settings, changes language to Japanese, and verifies it saved
AI ตอบ:
test('change language', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('combobox', { name: 'Language' }).selectOption('ja');
await page.getByRole('button', { name: 'Save preferences' }).click();
await expect(page.locator('.success-toast')).toContainText('Settings saved');
});
ดูเหมือนใช้ได้ — แต่ run แล้ว fail เพราะ:
- หน้าจริงไม่มี combobox name "Language" — มันคือ radio buttons
- ปุ่มจริงเขียน "บันทึก" ไม่ใช่ "Save preferences"
- ไม่มี
.success-toast— ระบบใช้ inline message
AI สร้างขึ้นจาก pattern ที่เห็นบ่อยใน app อื่น ไม่ได้เห็น app จริง
กัน: ส่ง HTML จริงให้ AI ดูทุกครั้ง อย่าให้เดา
test ที่ "ผ่าน" แต่ไม่ verify อะไร
test('user data displays correctly', async ({ page }) => {
await page.goto('/profile/123');
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('.profile-section')).toBeVisible();
await expect(page).toHaveURL(/\/profile\/123/);
});
ปัญหา: ไม่ assert content เลย หน้าโหลดอะไรก็ผ่าน
ถ้าเกิด bug ที่ profile page โชว์ข้อมูลของ user 124 แทน 123 → test ยังผ่าน
กัน: กฎใน prompt — "every assertion must check actual content, not just presence"
case ที่อันตรายที่สุด
ใช้ AI ใน CI ให้ auto-fix failing test:
Test failed. Fix the test so it passes.
AI เสนอ:
- await expect(page.getByTestId('total')).toHaveText('$199.99');
+ await expect(page.getByTestId('total')).toBeVisible();
Test "ผ่าน" แล้ว — แต่ feature ที่เพิ่ม discount logic เสีย, ราคาจาก $199.99 เป็น $20 (bug จริง) และ test ที่ควรจับได้ไม่จับ
กัน:
- ห้าม auto-fix test เด็ดขาด — fix ต้อง human review
- ใช้ AI เสนอ fix แต่ engineer commit เอง
- ห้าม AI ลบ assertion โดยไม่ approve
checklist กัน failure mode
ระดับ prompt:
- ห้าม AI เดา selector — ส่ง HTML จริงเสมอ
- ห้าม assertion ที่ใช้แค่
toBeVisible/toContain('')— ต้อง assert ค่าเฉพาะ - ขอ confidence + source สำหรับ claim ทุกข้อ
- ห้าม AI แก้ test เพื่อให้ผ่าน — แก้ implementation หรือ flag bug
ระดับ workflow:
- ทุก AI-generated test ต้อง human review ก่อน merge
- run AI-gen test 10 ครั้งติด ตรวจ flaky ก่อนเข้า main
- track origin ของ test (AI / human) เพื่อตามดู failure rate ทีหลัง
- audit AI usage รายเดือน — test ที่ AI เขียนมี false-pass rate สูงไหม
ระดับ infrastructure:
- AI ห้ามมี write access ไป main branch
- AI ห้าม approve PR ของตัวเอง
- log ทุก AI suggestion + decision ของ human (เก็บ audit trail)
กฎเดียวที่ใช้ได้กับทุกสถานการณ์
ก่อน trust output ของ AI ถามตัวเอง:
- ถ้า junior dev คนหนึ่งเขียนสิ่งนี้มา ฉันจะ approve โดยไม่อ่านไหม?
- ถ้าไม่ — ทำไม?
- AI ควรได้รับ scrutiny เท่ากันหรือมากกว่า
เหตุผล:
- junior dev มี accountability เป็นคน — แก้ error ได้, เรียนรู้
- AI ไม่มี accountability — error ไป production แล้วไม่มีใครรับ
- คุณคือคนที่จะรับผลของ output
ผลของ mental model นี้:
- ไม่ blindly trust แม้ AI ดูเชื่อถือได้
- ไม่ paranoid skeptic จนใช้ AI ไม่ได้
- ใช้ judgment ระหว่าง "งานง่ายเชื่อได้" กับ "งานสำคัญต้อง review"
แบบฝึก: spot the failure mode
ระบุ failure mode ในแต่ละ output ของ AI:
A:
test('login', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'pass');
await page.click('#submit');
expect(true).toBeTruthy();
});
B:
"I'm 95% confident this Playwright API exists: page.waitForCondition(() => navigator.onLine, { timeout: 5000 }). Use it for offline detection tests."
C:
test('create order', async ({ page }) => {
// ... setup
try {
await expect(page.getByTestId('order-id')).toBeVisible();
} catch {
console.log('Order ID not found, but test continues');
}
});
D:
"Your test failed because of a network issue. To fix, change await expect(price).toHaveText('$99') to await expect(price).toBeVisible()."
ดูคำตอบแนะนำ
A: Plausible-but-shallow — assertion เป็น expect(true).toBeTruthy() ผ่านเสมอ
B: Hallucination — page.waitForCondition ไม่มีใน Playwright API
C: Reward hacking — ใช้ try/catch กลบ failure, test ผ่านได้แม้ assertion fail
D: Reward hacking + plausible-but-wrong — เปลี่ยน assertion ให้อ่อนลงเพื่อ "fix" test แทนที่จะหา root cause จริง
สิ่งที่ต้องเอาติดตัวไปจากบทนี้
- 5 failure mode: hallucination, plausible-but-wrong, confident-but-uncertain, context loss, reward hacking
- Test ที่ "ผ่าน" ไม่เท่ากับ test ที่ "ถูก" เมื่อ AI gen
- ห้าม auto-fix test ด้วย AI — human review ทุกครั้ง
- ส่ง HTML จริง, ขอ confidence, ห้าม assertion อ่อน
- Mental model "ถ้า junior dev เขียน" ใช้ scrutiny ได้กับทุก output