Skip to main content

Test Pyramid

Unit vs Integration vs E2E​

Test pyramid คือแบบจำลองที่บอกว่า ควรมีเทสต์ประเภทไหนในสัดส่วนเท่าไหร่ เพื่อให้ได้ทั้ง ความเร็วและความมั่นใจพร้อมกัน

/\
/ \ E2E Tests (น้อยที่สุด, ช้าที่สุด, ครอบคลุม user flow จริง)
/----\
/ \ Integration Tests (ปานกลาง, ครอบ boundary ระหว่าง component)
/--------\
/ \ Unit Tests (เยอะที่สุด, เร็วที่สุด, ครอบ logic ย่อยทุกเส้นทาง)
/------------\
ระดับทดสอบอะไรความเร็วจำนวนที่ควรมี
Unitฟังก์ชัน/class เดียวแบบแยกตัว (isolated)มิลลิวินาทีเยอะที่สุด (หลักพัน)
Integrationหลาย component ทำงานร่วมกันจริง (เช่น service + database)วินาทีปานกลาง (หลักร้อย)
E2Eทั้งระบบทำงานจริงผ่าน user flow (เช่น browser automation)นาทีน้อยที่สุด (หลักสิบ)
🧠 จำง่ายๆ

"ยิ่งฐานกว้าง ยิ่งเร็วยิ่งถูก ยิ่งยอดแหลม ยิ่งช้ายิ่งแพงแต่มั่นใจสุด" — เหมือนตรวจสุขภาพ: ตรวจ เลือดพื้นฐาน (unit test) ทำได้บ่อย ถูก เร็ว ส่วนผ่าตัดตรวจ (E2E) ทำได้นานๆ ครั้ง แพง ใช้เวลานาน แต่ให้ข้อมูลที่ "ครอบคลุมความจริง" มากกว่า

🎯 Beyond: จุดที่ชาวบ้านไม่ค่อยรู้

Test pyramid แบบดั้งเดิม (Mike Cohn, 2009) ถูกวิจารณ์มากขึ้นเรื่อยๆ ในยุค microservices ว่า "ไม่สมจริง" เพราะระบบ distributed มักมีจุดบกพร่องที่เกิดจาก การสื่อสารระหว่าง service มากกว่า logic ภายใน service เดียว — แนวคิดใหม่ที่ได้รับความนิยมคือ "Testing Trophy" (Kent C. Dodds) ที่ขยาย integration test ให้ใหญ่กว่า unit test (สลับสัดส่วนจากพีระมิด) โดยให้ เหตุผลว่า integration test ให้ความมั่นใจต่อหน่วยเวลาที่ลงทุนสูงกว่า unit test ที่ mock เยอะเกินจนทดสอบแค่ "implementation detail" ไม่ใช่ "behavior จริงที่ user เจอ" — ไม่มีรูปทรง ไหนถูกต้องสัมบูรณ์ ขึ้นกับลักษณะระบบ (monolith vs microservices, legacy vs greenfield) การรู้ ทั้งสองแนวคิดและอธิบาย trade-off ได้คือสิ่งที่แยกคำตอบ intermediate จาก beginner

Trade-off ของแต่ละระดับ​

  • Unit test เยอะเกินไป (ไม่มี integration/E2E เลย) → ทุกเทสต์ผ่านแต่ระบบจริงพังเพราะ component เชื่อมกันไม่ถูก (เทสต์แต่ละชิ้นผ่านแต่ "ประกอบร่าง" แล้วพัง)
  • E2E เยอะเกินไป (ไม่มี unit test) → เทสต์ช้ามาก, flaky สูง (ขึ้นกับ environment/timing), debug ยากเวลา fail (ไม่รู้ว่าพังตรงไหนในระบบใหญ่)
  • Integration test หนักเกิน → ได้ความมั่นใจดี แต่ CI pipeline ช้าลงมากถ้าไม่จัดการ parallelization ให้ดี
🧠 จำง่ายๆ

"Unit test บอกว่าชิ้นส่วนแต่ละชิ้นดี, Integration test บอกว่าประกอบกันแล้วทำงานได้จริง, E2E บอกว่าลูกค้าใช้งานได้จริง" — ทั้งสามคำถามต่างกัน เทสต์ครบแค่ข้อเดียวตอบไม่ได้ทั้งสามคำถามพร้อมกัน

🎯 Beyond: จุดที่ชาวบ้านไม่ค่อยรู้

มี "ความล้มเหลว" ประเภทหนึ่งที่ unit test ไม่มีทางจับได้โดยหลักการ ไม่ว่าจะเขียนดีแค่ไหน: contract mismatch ระหว่าง service (เช่น service A ส่ง field ชื่อ user_id แต่ service B คาดหวัง userId) เพราะ unit test ของแต่ละฝั่ง mock อีกฝั่งไว้ตาม assumption ของตัวเอง ถ้า assumption ทั้งสองฝั่งผิดตรงกัน (ทั้งคู่ mock ผิดเหมือนกัน) unit test จะผ่านหมดแต่ระบบจริงพัง — นี่คือเหตุผลที่แนวคิด Contract Testing (เช่น Pact) เกิดขึ้นมา: ให้ consumer กำหนด "สัญญา" ที่ตัวเองคาดหวังจาก provider แล้วรัน verification ทั้งสองฝั่งแยกกันเทียบกับสัญญาเดียวกัน โดยไม่ต้อง deploy ทั้งระบบขึ้นมาเทสต์จริงแบบ E2E ที่แพงกว่ามาก — เป็นเทคนิคระดับ production ที่ ทีมระดับ intermediate ขึ้นไปควรรู้จักไว้

English Summary

The test pyramid prescribes many fast unit tests, fewer integration tests, and the fewest, slowest E2E tests. Over-indexing on unit tests risks all-green suites that still fail in production due to component miscommunication; over-indexing on E2E yields slow, flaky, hard-to-debug suites. The "Testing Trophy" (Kent C. Dodds) challenges the traditional pyramid by favoring integration tests for microservice-era systems, arguing they give more confidence per unit of effort than heavily- mocked unit tests. Neither shape is universally correct — it depends on system architecture. Notably, unit tests can never catch cross-service contract mismatches by design, since each side mocks the other based on its own (possibly wrong) assumptions — Contract Testing (e.g. Pact) exists specifically to close this gap without resorting to full E2E tests.