Unit Test สำหรับ API: มากกว่าเช็ก 200 คือเกราะกันบั๊กก่อน Deploy
Unit Test สำหรับ API ไม่ได้มีหน้าที่แค่ตรวจว่า endpoint ตอบกลับสำเร็จเท่านั้น แต่ยังช่วยจับปัญหาตั้งแต่ต้นทาง ทำให้ทีมพัฒนาแก้โค้ดได้มั่นใจและลดบั๊กซ้ำได้อย่างมีประสิทธิภาพ

Unit Test สำหรับ API: มากกว่าเช็ก 200 คือเกราะกันบั๊กก่อน Deploy
หลายคนเริ่มต้นการทดสอบ API ด้วยการยิงผ่าน Postman แล้วดูว่าผลลัพธ์ออกมาตามต้องการหรือไม่ วิธีนี้เหมาะมากสำหรับการทดลองใช้งานหรือเช็กการทำงานแบบเร็วๆ ในช่วงพัฒนา แต่เมื่อเข้าสู่ระบบจริงที่ต้องรันทดสอบซ้ำได้อัตโนมัติทุกครั้ง วิธีนี้เพียงอย่างเดียวยังไม่เพียงพอ
Unit Test จึงเข้ามามีบทบาทสำคัญ เพราะไม่ได้มีไว้แค่เช็กว่าเรียก API แล้วได้สถานะ 200 OK เท่านั้น แต่ยังช่วยให้ทีมตรวจจับบั๊กได้ตั้งแต่ก่อน deploy แบบเงียบๆ ลดความเสี่ยง และช่วยให้การเปลี่ยนแปลงโค้ดในอนาคตทำได้มั่นใจขึ้นมาก
ทำไม Unit Test สำหรับ API จึงสำคัญ
หัวใจของ Unit Test คือการทดสอบ "พฤติกรรมของโค้ด" ไม่ใช่การทดสอบระบบทั้งก้อนในครั้งเดียว หากออกแบบการทดสอบได้ดี เราจะรู้ได้ชัดว่าปัญหาเกิดที่จุดไหน และสามารถแก้ไขได้รวดเร็วกว่าเดิมมาก
ข้อดีที่เห็นได้ชัดของ Unit Test สำหรับ API ได้แก่
- ช่วยจับบั๊กก่อนขึ้นระบบจริง
- ทำให้ refactor โค้ดได้อย่างมั่นใจ
- ลดความผิดพลาดซ้ำในจุดเดิม
- ทำให้การพัฒนาร่วมกันในทีมมีมาตรฐานมากขึ้น
- รองรับการรันทดสอบอัตโนมัติใน CI/CD ได้ดี
3 ชั้นที่ควรแยกให้ออกเวลาเทส API
เมื่อต้องทดสอบ API endpoint ควรทำความเข้าใจว่าแต่ละส่วนมีหน้าที่ต่างกัน และไม่ควรจับทุกอย่างมาทดสอบรวมกันทั้งหมดตั้งแต่ต้น
1. Route หรือ Controller
ส่วนนี้มีหน้าที่รับ request แล้วส่งต่อไปยังส่วนที่เกี่ยวข้องอย่างถูกต้อง เช่น รับพารามิเตอร์จากผู้ใช้ ตรวจรูปแบบเบื้องต้น และคืน response กลับ
สิ่งที่ควรทดสอบ เช่น
- รับ request ถูกต้องหรือไม่
- เรียก service ที่เกี่ยวข้องหรือไม่
- ส่ง status code และ response ถูกต้องหรือไม่
2. Service หรือ Business Logic
นี่คือส่วนสำคัญของกฎธุรกิจ เช่น การตรวจเงื่อนไข การคำนวณ การแปลงข้อมูล หรือการตัดสินใจต่างๆ ในระบบ
สิ่งที่ควรทดสอบ เช่น
- อนุญาตหรือปฏิเสธการทำงานตามเงื่อนไขที่กำหนด
- ตรวจกรณีข้อมูลซ้ำหรือข้อมูลผิดรูปแบบ
- จัดการข้อผิดพลาดจาก dependency ได้ถูกต้อง
3. Repository หรือ Database Layer
ชั้นนี้เกี่ยวข้องกับการเข้าถึงฐานข้อมูล เช่น query, insert, update หรือ delete ซึ่งโดยธรรมชาติมักใกล้เคียงกับ Integration Test มากกว่า Unit Test
แม้จะยังทดสอบได้ แต่ถ้าต้องเชื่อมต่อฐานข้อมูลจริงทุกครั้ง ก็อาจทำให้การทดสอบช้าและไม่เสถียร
สิ่งที่มือใหม่มักพลาดในการเขียน Unit Test
ข้อผิดพลาดที่พบได้บ่อยคือปล่อยให้ Unit Test ไปยิงฐานข้อมูลจริง หรือเรียก API ภายนอกจริงทุกครั้งที่รัน
สิ่งนี้ทำให้เกิดปัญหาหลายด้าน เช่น
- ทดสอบช้าเกินไป
- ผลลัพธ์ไม่นิ่ง
- พังง่ายเมื่อสภาพแวดล้อมเปลี่ยน
- แยกสาเหตุของปัญหาได้ยาก
แนวทางที่นิยมคือการ mock dependency เช่น
- mock database
- mock payment service
- mock email sender
เมื่อ mock ส่วนที่อยู่นอกขอบเขตการทดสอบ เราจะโฟกัสได้เต็มที่กับพฤติกรรมของโค้ดที่ต้องการตรวจจริงๆ
ตัวอย่างกรณีทดสอบ API สร้างผู้ใช้ใหม่
สมมุติว่ามี API สำหรับสร้างผู้ใช้ใหม่ สิ่งที่ควรทดสอบอาจประกอบด้วย
- ถ้าข้อมูลครบ ระบบตอบ
201และคืนuser id - ถ้า email ซ้ำ ระบบตอบ
409 - ถ้า format email ไม่ถูก ระบบตอบ
400 - ถ้า service ล่ม ระบบตอบ
500
ประเด็นสำคัญคือ เราไม่ได้ทดสอบแค่ว่า "ระบบทำงานผ่าน" แต่ต้องทดสอบด้วยว่า "ระบบล้มเหลวอย่างถูกต้อง" ในสถานการณ์ที่คาดไว้
โครงสร้างความคิดที่ช่วยออกแบบเทสได้ดี
รูปแบบที่เข้าใจง่ายคือ
Request เข้า ➜ Validate ➜ Call Service ➜ Return Response
แต่ละจุดมีประเด็นที่ควรตรวจต่างกัน
Validate
ควรตรวจเรื่อง
- required fields
- type ของข้อมูล
- format เช่น email หรือเบอร์โทร
Service
ควรตรวจเรื่อง
- กฎธุรกิจ
- เงื่อนไขการอนุญาต
- ข้อจำกัดเฉพาะของระบบ เช่น ห้ามสมัครซ้ำ หรือห้ามยอดติดลบ
Response
ควรตรวจเรื่อง
- status code
- รูปแบบ JSON
- field สำคัญใน response
- ข้อความหรือ error code ที่ระบบตกลงใช้ร่วมกัน
การ assert ที่ดีต้องมากกว่า status code
หลายทีมมักหยุดอยู่ที่การเช็กเพียง status code แต่จริงๆ แล้วนั่นยังไม่พอ เพราะ response ที่ถูกต้องควรเป็นไปตาม contract ที่กำหนดไว้ด้วย
ตัวอย่างสิ่งที่ควร assert เพิ่ม เช่น
- มี field
success - มี
dataเมื่อสำเร็จ - มี
errorเมื่อเกิดปัญหา - โครงสร้าง JSON ตรงตามที่ frontend หรือ consumer คาดหวัง
อย่างไรก็ตาม ไม่ควรเขียน test ให้ผูกกับข้อความ error แบบตรงตัวทุกอักษรมากเกินไป เพราะหากมีการแก้คำเล็กน้อย เทสอาจพังทั้งที่พฤติกรรมหลักยังถูกต้องอยู่
แนวทางที่ดีกว่าคือเช็กเฉพาะ
- key สำคัญ
- error code
- รูปแบบข้อมูลที่มีความหมายต่อระบบ
เครื่องมือยอดนิยมในแต่ละภาษา
แต่ละภาษามีเครื่องมือที่ช่วยให้การเทส API สะดวกขึ้น เช่น
- Node.js: Jest ร่วมกับ Supertest
- Python: pytest ร่วมกับ FastAPI TestClient หรือ Flask test client
- Java: JUnit + MockMvc
การเลือกเครื่องมือที่เหมาะกับ stack ของทีม จะช่วยให้เริ่มเขียนเทสได้ง่ายและต่อยอดได้เร็ว
สูตรจำง่ายของ Unit Test API ที่ดี
Unit Test ที่ดีควรมีคุณสมบัติหลักดังนี้
- เร็ว: รันแล้วไม่ต้องรอนาน
- แยกขาด: ไม่พึ่ง dependency ภายนอกโดยไม่จำเป็น
- คาดเดาได้: ผลลัพธ์ชัดเจน
- รันซ้ำแล้วได้ผลเหมือนเดิม: ไม่ขึ้นกับเน็ต ฐานข้อมูล หรือ service ภายนอก
ถ้าเทสหนึ่งตัวต้องรอหลายวินาที หรือขึ้นกับ environment ภายนอกมากเกินไป นั่นมักไม่ใช่ Unit Test ในความหมายที่แท้จริงแล้ว
การตั้งชื่อเทสให้สื่อพฤติกรรมชัดเจน
อีกเทคนิคที่ช่วยทีมได้มากคือการตั้งชื่อเทสให้บอกสิ่งที่คาดหวังอย่างตรงไปตรงมา เช่น
should return 400 when email format is invalid
ชื่อแบบนี้มีข้อดีมาก เพราะเมื่อเปิดรายงานผลเทส ทุกคนในทีมจะเข้าใจได้ทันทีว่าเทสนั้นกำลังตรวจสอบพฤติกรรมอะไรอยู่ โดยไม่ต้องเปิดโค้ดลงไปดูทุกครั้ง
ควรเริ่มเขียนเทสจากตรงไหนก่อน
สำหรับทีมที่เพิ่งเริ่ม ไม่จำเป็นต้องพยายามทดสอบทุก endpoint พร้อมกันในวันแรก เพราะจะทำให้รู้สึกหนักเกินไปและเริ่มต้นยาก
ทางที่เหมาะสมกว่าคือเริ่มจาก endpoint ที่สำคัญต่อธุรกิจ เช่น
- login
- register
- create order
- payment callback
จุดเหล่านี้มักเป็นส่วนที่หากพังแล้วจะกระทบผู้ใช้และธุรกิจโดยตรง การเริ่มจากส่วนสำคัญก่อนจึงให้ผลคุ้มค่ามากที่สุด
สรุป
Unit Test สำหรับ API ไม่ได้มีไว้แค่ตรวจว่าระบบตอบ 200 หรือไม่ แต่คือเครื่องมือสำคัญที่ช่วยยกระดับคุณภาพโค้ดของทีมในระยะยาว การทดสอบที่ดีควรโฟกัสพฤติกรรมของโค้ด แยกส่วนที่ต้องทดสอบให้ชัด ใช้ mock อย่างเหมาะสม และตรวจทั้งกรณีสำเร็จและกรณีล้มเหลว
แม้ Unit Test จะไม่สามารถทำให้โค้ดปลอดบั๊กได้ 100% แต่ช่วยให้ทีมแก้ไข ปรับปรุง และพัฒนาระบบได้อย่างมั่นใจมากขึ้น เมื่อทำอย่างต่อเนื่อง เทสจะกลายเป็นเหมือนตาข่ายนิรภัยของโปรเจกต์ ที่ช่วยลดความเสี่ยงและเพิ่มความเร็วในการพัฒนาได้อย่างชัดเจน