Back to notes
mastery-backend-nestjs
Featured

NestJS Testing Strategy: Unit Test vs E2E Test

NestJS ให้ Jest มาตั้งแต่เกิด! เรียนรู้วิธี Mock Dependencies เพื่อทำ Unit Test และการยิง API จริงด้วย Supertest (E2E)

January 29, 20262 min read readNNexis by Seereen

🛑 1. The Problem First: "แก้ตรงนี้ พังตรงนู้น" (Regression Fear)

ลองนึกถึงวันที่คุณต้องแก้ Logic การคำนวณราคาโปรโมชันในไฟล์ขนาดยักษ์:

HLJS TYPESCRIPT
// ❌ Naive Approach: แก้โค้ดแล้วกด Refresh หน้าจอเพื่อเช็คด้วยมือ
if (isJunior) price *= 0.5; // แก้เสร็จ ลองกดซื้อ... อ้าว! ทำไมราคาสมาชิกระดับ Gold พังล่ะ?

ปัญหา: การหวังพึ่งพา "ความจำ" และ "การกดเช็คด้วยมือ" (Manual Test) คือความเสี่ยงที่รับไม่ได้ในระดับ Enterprise ยิ่งโค้ดเยอะขึ้น ทุุกครั้งที่คุณแก้บั๊ก 1 จุด คุณกำลังมีโอกาสสร้างบั๊กใหม่เพิ่มอีก 2 จุดเสมอ ถ้าไม่มีระบบตรวจสอบอัตโนมัติ คุณจะเสียเวลา 80% ของวันไปกับการ "ไล่ซ่อมของเก่า" แทนที่จะได้เขียนฟีเจอร์ใหม่ครับ


💡 2. Real-Life Analogy: การตรวจสภาพรถและสนามจำลอง

  • Unit Testing: เหมือนการ "ถอดหัวเทียนออกมาเช็ค". คุณเอาชิ้นส่วนเล็กๆ 1 ชิ้นออกมาทดสอบในสภาพแวดล้อมที่ควบคุมได้ (Lab) เพื่อให้แน่ใจว่าหัวเทียนนี้ทำงานได้เป๊ะๆ โดยไม่ต้องสนใจว่าเครื่องยนต์พังไหม
  • Integration/E2E Testing: เหมือนการ "เอารถไปวิ่งในสนามทดสอบ". คุณไม่ได้ดูแค่หัวเทียน แต่ดูทั้งระบบ ตั้งแต่การบิดกุญแจ, คันเร่ง, เบรก ไปจนถึงล้อหมุนจริง เพื่อดูว่าทุุกส่วนทำงาน "ประสานกัน" ได้หรือไม่
  • Mocking: เหมือนการ "ใช้ตุ๊กตา Dummy". แทนที่จะเอาคนจริงๆ ไปชนใน Test Drive คุณใช้ตุ๊กตาแทน เพื่อจำลองสถานการณ์โดยไม่ทำให้ใครบาดเจ็บ (เหมือนจำลอง Database เพื่อไม่ให้ข้อมูลจริงพัง)

🚀 3. Execution Journey: เส้นทางสู่ Code ที่ไม่มีวันพ่ายแพ้

เราจะสร้าง "รั้วป้องกัน" ให้โค้ดของเราด้วย 3 เลเยอร์

🛠 Step-by-step:

  1. Isolation (Unit Test): ใช้ Test.createTestingModule เพื่อสร้าง Lab จำลองและ Mock ของที่อยู่นอกตัวแปร (เช่น Database) ออกไปให้หมด
  2. Flow Validation (E2E Test): ใช้ Supertest ยิง HTTP จริงๆ เข้าไปที่ Controller เพื่อดูว่า Middleware ผ่านไหม และ Response สวยไหม
  3. Continuous Verification: รัน npm test ทุุกครั้งก่อนจะ Push งาน เพื่อให้แน่ใจว่า "โค้ดใหม่ไม่ทำลายโลกเก่า"
HLJS TYPESCRIPT
// ✅ Best Practice: Mock Dependency เพื่อความเร็ว (Unit Test)
repoMock = {
  find: jest.fn().mockResolvedValue([{ id: 1, name: "Test User" }]),
};

const module = await Test.createTestingModule({
  providers: [
    UsersService,
    { provide: getRepositoryToken(User), useValue: repoMock }, // 🛠 สลับ DB จริงด้วยหุ่นจำลอง
  ],
}).compile();

🪤 4. The Junior Trap: โรค "เขียน Test ให้ครบ 100% (Fake Coverage)"

จูเนียร์มักจะคลั่งไคล้ตัวเลข Coverage จนเผลอเขียน Test ที่ "ไม่ได้เทสอะไรเลย":

HLJS TYPESCRIPT
// ❌ Junior Trap: เทสแค่ให้เขียว (Pass) แต่ไม่ได้เช็ค Logic
it("should define service", () => {
  expect(service).toBeDefined(); // 🌋 เขียวนะ! แต่ถ้า Logic ข้างในพัง Test นี้ก็ไม่ช่วยอะไรเลย
});

ระวัง: ตัวเลข 100% Coverage ไม่ได้หมายความว่าโค้ดไม่มีบั๊ก ✅ การแก้ไข: โฟกัสการเทสที่ "Edge Cases" (เช่น ถ้าข้อมูลว่างต้องทำไง?) และ "Business Logic" สำคัญๆ มากกว่าการเทสแค่ว่าตัวแปรมีอยู่จริงไหม


⚖️ 5. The Why Matrix: เลือกเทสตอนไหนดี?

หัวข้อUnit Test (เร็วและแม่น)E2E Test (ช้าและกว้าง)
ความเร็ว⚡⚡⚡ เร็วระดับ Milliseconds🐢 ช้า ต้องโหลดทุุกโมเดล
ความแม่นยำสูงมาก (รู้จุดที่พังทันที)ต่ำ (พังที่จุดหนึ่ง แต่อาจฟ้องที่อื่น)
ความครอบคลุมแคบ (เฉพาะฟังก์ชัน)กว้าง (ทั้งระบบ)
สัดส่วนที่ควรมี70% (ฐานราก)20-30% (ตรวจสอบ Flow สำคัญ)

🎓 6. Senior Mindset Summary

การเป็น Senior คือการมองว่า "เราไม่ได้เขียน Test เพื่อหาบั๊ก แต่เราเขียนเพื่อสร้าง 'ใบรับประกัน' ให้โค้ดของเรา". ถ้าคุณเขียน Test ไม่ได้ นั่นคือสัญญาณเตือนว่าโค้ดของคุณพึ่งพาของข้างนอกมากเกินไป (High Coupling) จงกลับไปแก้ Architecture แล้ว Test จะกลายเป็นเรื่องสนุกเองครับ!

Share this note

© 2026 My Notes by Seereen