CupCode.cc
Section 05 · Module 03 · Lesson 02 · ~22–26 นาที

แปลง Manual Test Steps เป็น Automation ด้วย AI

บทนี้พาคุณใช้ AI ลดเวลาแปลง manual test ที่มีอยู่แล้วเป็น automation script โดยรักษาความหมายของ test ไว้ ไม่ใช่แค่ port code ตามตัวอักษร

เวลาเรียน: 22–26 นาที ต้องรู้มาก่อน: Lesson 01 - ใช้ AI สร้าง Playwright Script รูปแบบ: อ่าน + walkthrough conversion + แบบฝึกแปลงจริง

จบบทนี้แล้วคุณจะ...

  • มี workflow ในการแปลง manual test case ให้เป็น automation script ด้วย AI
  • รู้ว่าเมื่อไหร่ควร "แปลงตรง" และเมื่อไหร่ควร "เขียนใหม่"
  • รักษา intent ของ manual test ไว้แม้ว่า script ที่ได้จะหน้าตาต่าง
  • จัดการ test data, precondition และ cleanup ใน automation อย่างถูก
1 · ทำไม "แปลงตรง" มักได้ test ที่แย่

Manual test ไม่ได้ออกแบบให้ run อัตโนมัติ

Manual test step มักเขียนแบบ:

1. เปิดเว็บ
2. คลิกปุ่ม Login ที่มุมขวาบน
3. รอให้หน้า login โหลด
4. กรอก email
5. กรอก password
6. คลิก Submit
7. รอประมาณ 3 วินาที
8. เห็นหน้า dashboard

ถ้าแปลงตรงเป็น Playwright จะได้ script ที่:

  • มี waitForTimeout(3000) ที่ไม่จำเป็น
  • assertion ขั้นสุดท้ายแค่ "เห็นหน้า dashboard" — กว้างไป
  • ไม่มี data setup, ใช้ user อะไรก็ได้
  • ถ้า fail ไม่รู้ว่าพังขั้นไหน

ปัญหา: manual test เขียนเพื่อ "บอกให้คนทำตาม" ไม่ใช่ "บอก machine ให้ verify behavior" ความตั้งใจคือเหมือน แต่รูปแบบต้องเปลี่ยน

2 · 3 ขั้นในการแปลง manual → automation

โครงที่ใช้ได้กับทุก test case

ขั้น 1: ดึง intent ของ test

อ่าน manual step แล้วถาม: "test นี้ตั้งใจ verify อะไร"

คำตอบควรเป็น 1 ประโยค:

  • "user ที่มี account valid login แล้วเห็น dashboard ของตัวเอง"
  • "user ที่ใส่ password ผิด 3 ครั้งจะถูก lock 15 นาที"
  • "guest user เพิ่มสินค้าใส่ตะกร้าได้โดยไม่ต้อง login"

intent นี้คือสิ่งที่ test ใหม่ต้องรักษาไว้ ส่วนที่เหลือเปลี่ยนได้

ขั้น 2: ระบุ data + precondition + cleanup

manual test มักไม่ระบุชัด แต่ automation ต้องมี:

  • Data: user ใช้ account ไหน? balance เท่าไหร่? state ของระบบ?
  • Precondition: ต้องมี cart ว่าง? login มาก่อน?
  • Cleanup: หลัง test เสร็จต้อง reset อะไร?

ถ้าไม่ระบุ test จะ flaky หรือ pollute test อื่น

ขั้น 3: prompt AI ให้แปลงโดยใส่ทั้ง intent และ data

Convert this manual test to a Playwright TypeScript test.

Manual test:
[PASTE]

Intent of this test (most important):
"User with valid credentials logs in and sees their personalized dashboard"

Data:
- Test user: [email protected] / Pass1234
- Expected dashboard greeting: "Welcome back, Test User"

Convention:
- Use getByRole and getByTestId
- No waitForTimeout
- Add explicit assertions for each milestone

Output: single test file.

AI จะ gen script ที่รักษา intent ไม่ใช่แค่ port step

3 · ตัวอย่าง before / after เต็ม

ดูความต่างที่จับต้องได้

Manual test:

TC-LOGIN-001: User can login with valid credentials
1. เปิด https://app.example.com
2. คลิก "Login" มุมขวาบน
3. กรอก email "[email protected]"
4. กรอก password "Pass1234"
5. คลิก Submit
6. รอประมาณ 3 วินาที
7. ตรวจสอบว่าเห็นหน้า dashboard

แปลงตรง (แย่):

test('login', async ({ page }) => {
  await page.goto('https://app.example.com');
  await page.click('text=Login');
  await page.fill('#email', '[email protected]');
  await page.fill('#password', 'Pass1234');
  await page.click('button:has-text("Submit")');
  await page.waitForTimeout(3000);
  await expect(page.locator('.dashboard')).toBeVisible();
});

แปลงโดยรักษา intent (ดี):

import { test, expect } from '@playwright/test';

test.describe('Login', () => {
  test('user with valid credentials sees personalized dashboard', async ({ page }) => {
    await page.goto('/');

    await page.getByRole('link', { name: 'Login' }).click();
    await expect(page).toHaveURL(/\/login/);

    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('Pass1234');
    await page.getByRole('button', { name: 'Sign in' }).click();

    await expect(page).toHaveURL(/\/dashboard/);
    await expect(
      page.getByRole('heading', { name: /Welcome back, Test User/ })
    ).toBeVisible();
  });
});

ความต่างที่สำคัญ:

  • มี explicit assertion หลังคลิก Login (URL เปลี่ยนหรือยัง)
  • ไม่มี hard wait
  • assert content เฉพาะ ("Welcome back, Test User") ไม่ใช่แค่ element มี
  • locator stable
4 · เมื่อไหร่ "ไม่ควร" แปลง

บาง test ดีกว่าเขียนใหม่ตั้งแต่ต้น

ห้ามแปลง:

  • Exploratory test: test ที่ตั้งใจให้คนใช้ judgment ระหว่างทำ — automation ทำแทนไม่ได้
  • Visual perception test: "ตรวจว่า UI สวย" / "หา bug ที่ไม่คาด" — automation ไม่ทำแทนคน
  • Test ที่เขียนไว้นานแล้ว irrelevant: feature เปลี่ยนไปแล้ว — เขียน test ใหม่ตาม feature ปัจจุบัน
  • Test ที่ verify "feel": "user feels confident" / "system seems fast" — assertion วัดไม่ได้

ถ้าเจอ manual test แบบนี้ แทนที่จะแปลง ให้คุยกับทีมว่ายังจำเป็นไหม หรือต้องเขียนใหม่ในรูปแบบที่ automate ได้

5 · จัดการ test data ใน batch conversion

เมื่อต้องแปลง 50+ test cases

ถ้ามี manual test เยอะที่จะแปลง อย่า prompt ทีละอัน:

  1. ระบุ data ที่ต้องใช้ทั้งหมดก่อน — gen test users / fixtures ครั้งเดียว
  2. group test ที่ใช้ flow คล้ายกัน — แปลงพร้อม shared Page Object
  3. แปลง happy path ก่อน, edge case ทีหลัง
  4. หลังแปลงครบ run ทั้งชุดดู flaky test ที่ต้องแก้ก่อน merge

วิธีนี้ลดเวลา review เพราะ pattern ของ test ทั้ง batch จะใกล้กัน

Practice

แบบฝึก: แปลง manual test เป็น Playwright

แปลง manual test ด้านล่างเป็น Playwright test ที่มี intent ชัดและไม่มี anti-pattern:

TC-CART-005: Guest user adds item to cart
1. เปิด /products
2. คลิกที่สินค้าชิ้นแรก
3. รอให้หน้า detail โหลด
4. คลิก "Add to cart"
5. รอ 2 วินาที
6. ตรวจสอบว่าตะกร้ามีสินค้า

ระบุ intent ก่อน แล้วเขียน script

ดูคำตอบแนะนำ

Intent: "Guest user (ไม่ login) สามารถเพิ่มสินค้าจาก product list ลงตะกร้าได้ และตะกร้าแสดงจำนวนถูกต้อง"

Data:

  • ไม่มี user login
  • product ใด ๆ ที่มี stock > 0

Script:

import { test, expect } from '@playwright/test';

test.describe('Cart', () => {
  test('guest user can add a product to cart from product list', async ({ page }) => {
    await page.goto('/products');

    const firstProduct = page.getByTestId('product-card').first();
    const productName = await firstProduct.getByRole('heading').textContent();

    await firstProduct.click();
    await expect(page.getByRole('heading', { name: productName ?? '' })).toBeVisible();

    await page.getByRole('button', { name: 'Add to cart' }).click();

    await expect(page.getByTestId('cart-count')).toHaveText('1');
    await page.getByRole('link', { name: /cart/i }).click();
    await expect(page.getByText(productName ?? '')).toBeVisible();
  });
});

ตรวจ intent: assert ทั้งจำนวนใน cart icon + ตรวจว่าสินค้าที่เพิ่มเข้าไปคือสินค้าที่คลิก

สิ่งที่ต้องเอาติดตัวไปจากบทนี้

  • แปลงตรง manual → automation มักได้ test ที่ flaky และ assert อ่อน
  • ดึง intent ของ manual test ก่อน อย่า port step ตามตัวอักษร
  • ระบุ data, precondition, cleanup ที่ manual test ละไว้
  • บางประเภทของ manual test ไม่ควรแปลง (exploratory, visual)
  • batch conversion ต้องวางแผน fixture และ shared object ก่อน