~/VibeHandbook
PDF ฟรี

บท 15 · 02

การทดสอบ: ตาข่ายนิรภัยของคุณ

การทดสอบ (test) คือโค้ดที่ตรวจสอบว่าโค้ดอื่นทำงานตามที่คุณคาดหวังหรือไม่ เมื่อเขียนไว้แล้ว มันจะรันภายในไม่กี่วินาที และบอกคุณทันทีถ้ามีอะไรถดถอย (regress)

ชุดทดสอบที่ดีจะมีลักษณะเหมือนพีระมิด: ฐานเป็นการทดสอบระดับหน่วย (unit test) เล็ก ๆ เร็ว ๆ จำนวนมาก ตรงกลางเป็นการทดสอบเชิงบูรณาการ (integration test) จำนวนน้อยลง และบนยอดเป็นการทดสอบแบบครบวงจร (end-to-end test) ที่ช้าและมีจำนวนน้อยที่สุด ความปลอดภัยส่วนใหญ่ของคุณมาจากฐานที่ราคาถูก:

              ╱╲
             ╱  ╲        E2E         few · slow · whole app
            ╱────╲       (end-to-end)
           ╱      ╲
          ╱  INTEG ╲     INTEGRATION  some · medium
         ╱──────────╲    (parts together)
        ╱            ╲
       ╱     UNIT     ╲  UNIT          many · fast · one function
      ╱────────────────╲
         cheap base = most of your tests

คุณไม่จำเป็นต้องมี coverage 100% เริ่มจากการทดสอบสิ่งที่จะสร้างความเสียหายมากที่สุดถ้ามันพัง: การล็อกอิน การชำระเงิน ข้อมูลที่สร้างขึ้นใหม่ไม่ได้ และตรรกะธุรกิจหลัก AI เก่งมากในการเขียนเทสต์ — บางทีเก่งกว่าการเขียนฟีเจอร์ด้วยซ้ำ เพราะพฤติกรรมที่คาดหวังนั้นชัดเจนเป็นรูปธรรม

นี่คือพรอมป์ตัวอย่างที่ใช้ได้ผลดี:

Write tests for the calculateDiscount function in src/pricing.js.
Cover: a normal case, the zero/empty case, the maximum-discount
boundary, and one invalid input. Use the existing test setup in
this repo (check how other *.test.js files are structured).
Run the tests and show me they pass before you finish.

บรรทัดสุดท้ายนั้นสำคัญ ให้ AI รัน เทสต์เสมอ ไม่ใช่แค่เขียนมันขึ้นมา เทสต์ที่ไม่เคยถูกรันคือการเดา และก็คุ้มค่าที่จะระบุให้ชัดเจนว่ากรณีไหนที่ต้องครอบคลุม เพราะถ้าปล่อยให้ AI ตัดสินใจเอง มันมักจะเขียนสามแบบของเส้นทางการใช้งานปกติแล้วเรียกว่าครอบคลุมแล้ว กรณีที่จับบั๊กได้จริงคือกรณีน่าเบื่อ ๆ: อินพุตว่างเปล่า ค่าขอบเขต สิ่งที่ควรโยนข้อผิดพลาด (throw)

ตัวอย่างง่าย ๆ ของสิ่งที่มันควรผลิตออกมา:

import { calculateDiscount } from "./pricing.js";
import { describe, it, expect } from "vitest";

describe("calculateDiscount", () => {
  it("applies a normal percentage discount", () => {
    expect(calculateDiscount(100, 0.2)).toBe(80);
  });

  it("returns the full price when discount is zero", () => {
    expect(calculateDiscount(100, 0)).toBe(100);
  });

  it("never discounts below zero", () => {
    expect(calculateDiscount(100, 1.5)).toBe(0);
  });

  it("rejects a negative price", () => {
    expect(() => calculateDiscount(-10, 0.2)).toThrow();
  });
});

เมื่อคุณเปลี่ยนฟีเจอร์ในภายหลัง ให้รันเทสต์ก่อน ถ้ามันขึ้นสีแดง แปลว่าคุณเจอจุดพังก่อนที่ผู้ใช้ของคุณจะเจอ เมื่อ AI แก้บั๊ก ให้ขอให้มันเพิ่มเทสต์ที่จะล้มเหลวกับพฤติกรรมเดิม — เพื่อให้บั๊กนั้นไม่สามารถแอบกลับมาได้อีก นิสัยนี้มีชื่อเรียกว่า regression test (เทสต์กันบั๊กย้อนกลับ) บั๊กทุกตัวที่คุณเจอคือบทเรียนที่โค้ดเบสได้จ่ายเงินซื้อมาแล้ว การมี regression test คือวิธีที่คุณเก็บรักษาบทเรียนนั้นไว้

ข้อควรระวังหนึ่งข้อ: เทสต์เองก็ผิดได้เหมือนกัน ถ้า AI เขียนเทสต์ที่ยืนยันว่าพฤติกรรม ที่มีบั๊ก นั้นถูกต้อง มันจะผ่านตลอดไปในขณะที่แอปของคุณยังพังอยู่ ดังนั้นให้อ่านเงื่อนไขการยืนยัน (assertion) ไม่ใช่แค่เครื่องหมายถูกสีเขียว เทสต์ที่ผ่านหมายความแค่ว่า "โค้ดทำในสิ่งที่เทสต์บอก" — คุณยังต้องยืนยันว่าเทสต์นั้นบอกในสิ่งที่ถูกต้อง

อยากได้แบบออฟไลน์?

ดาวน์โหลดหนังสือทั้งเล่มเป็น PDF หรือ EPUB — ฟรี