Supertest · เริ่มต้นกับ Supertest
ขอบเขตของ E2E Test
ทำความเข้าใจว่า E2E test ตรวจ HTTP contract, module wiring และ runtime flow ตรงไหน
E2E boundary คือเส้นทางที่เราตั้งใจพิสูจน์
E2E ย่อมาจาก End-to-End หมายถึงการตรวจเส้นทางตั้งแต่จุดเริ่มต้นของผู้ใช้หรือ client ไปจนถึงผลลัพธ์ปลายทาง ใน track นี้ E2E test คือการส่ง HTTP request เข้า NestJS application แล้วตรวจ HTTP response โดยปล่อยให้ request ผ่านชั้นสำคัญตาม boundary ที่ suite เลือก
ชื่อชั้นเป็นภาพรวมเพื่อช่วยวางแผน test ไม่ได้หมายความว่าทุก project ต้องมีโครงสร้างเหมือนกันทุกชั้น
สิ่งที่ E2E test พิสูจน์คือระบบที่ประกอบแล้วตอบ caller ได้ถูกต้องหรือไม่ ไม่ใช่การตรวจทุกบรรทัดหรือทุก private method หากต้องการตรวจรายละเอียดภายในชั้นใด ให้เลือก unit หรือ integration test ที่เหมาะกับคำถามนั้น
เปรียบเทียบ unit, integration และ E2E
| รูปแบบ | ตัวอย่างการเริ่มทดสอบ | สิ่งที่หลักฐานบอกเรา |
|---|---|---|
| Unit | `service.createTodo(dto)` | logic ของหน่วยเล็กทำงานถูกภายใต้ dependency ที่ควบคุมได้ |
| Integration | service + repository จริงหรือ adapter จริง | ส่วนประกอบหลายชั้นทำงานร่วมกันได้ |
| E2E / HTTP | `request(server).post('/todos')` | caller ส่ง request แล้วได้ behavior และ response ตาม public contract |
ไม่มี boundary ใดดีที่สุดสำหรับทุกคำถาม Unit test มักเร็วและชี้จุดแคบกว่า ส่วน E2E ให้ความมั่นใจว่า route, runtime wiring และ response ทำงานร่วมกันจริง แต่ต้องเตรียม application และ state มากกว่า การเลือก boundary จึงต้องเริ่มจากสิ่งที่อยากพิสูจน์
E2E ไม่ได้แปลว่าต้องต่อทุกระบบภายนอก
เราสามารถเลือกให้ database จริงอยู่ใน boundary หรือควบคุม dependency บางตัวได้ แต่ต้องระบุให้ชัดว่าหลักฐานของ test ครอบคลุมถึงไหน ถ้า override repository ทั้งหมด test นั้นไม่ได้พิสูจน์ PostgreSQL จริง
direct call กับ HTTP request ให้หลักฐานคนละแบบ
ทั้งสองแบบอาจเรียก business logic เดียวกัน แต่คำตอบที่ได้จาก test ไม่เหมือนกัน
| คำถาม | direct call | HTTP request |
|---|---|---|
| route mapping ถูกหรือไม่ | ตอบไม่ได้ | ตอบได้ |
| validation/guard ทำงานหรือไม่ | ต้องเรียกเองหรือไม่อยู่ใน scope | ตอบได้เมื่ออยู่ใน app runtime |
| status และ error shape ถูกหรือไม่ | ไม่ได้เป็น HTTP response | ตอบได้จาก response |
| business logic ถูกหรือไม่ | ตอบได้ตรงและแคบกว่า | ตอบได้ผ่านพฤติกรรมปลายทาง |
เลือก assertion ให้ตรงกับ boundary
เมื่อเขียน E2E ให้ assert ผลลัพธ์สาธารณะที่ caller พึ่งพา เช่น status code, response body, response headers และผลที่อ่านกลับได้จาก endpoint ถัดไป หลีกเลี่ยงการ assert private method, object reference หรือจำนวนครั้งที่ method ภายในถูกเรียก หากสิ่งนั้นไม่ใช่ contract ที่ client รู้จัก
ตัวอย่างนี้ตรวจผลลัพธ์ของ HTTP contract และไม่ผูกกับชื่อ class หรือวิธีบันทึกข้อมูลภายใน
กฎสำคัญ
ตรวจ behavior ที่ caller พึ่งพา
เช่น POST สำเร็จแล้วได้ `201` และ response มี Todo ที่สร้างตาม public fields
อย่าเลื่อน boundary โดยไม่ตั้งใจ
ถ้า mock หรือ override dependency ให้บอกว่า test ลดขอบเขตตรงไหน และอย่าสรุปผลเกินหลักฐานที่มี
หนึ่ง test ควรมีคำถามหลัก
แยก route, validation, success และ not-found behavior ให้ failure อ่านได้ง่ายก่อนค่อยรวมเป็น flow ใหญ่ใน phase CRUD