กลับไปหน้าบทความ
#Unit Test#การทดสอบซอฟต์แวร์#Refactor#Regression Test#คุณภาพโค้ด

เขียน Unit Test แบบไหนก่อน จึงคุ้มที่สุดเมื่อโปรเจกต์เริ่มโต

เมื่อโปรเจกต์เริ่มซับซ้อน การเขียน Unit Test ให้ถูกจุดสำคัญกว่าการไล่ทำ coverage ให้สวย เพราะ Test แต่ละแบบมีหน้าที่ต่างกัน และช่วยลดความเสี่ยงคนละมุม การวางลำดับให้เหมาะจะช่วยให้ทีมมั่นใจเวลาแก้โค้ดและลดโอกาสเกิด incide

22 พฤษภาคม 2569อ่านประมาณ 2 นาที

แชร์บทความ

เขียน Unit Test แบบไหนก่อน จึงคุ้มที่สุดเมื่อโปรเจกต์เริ่มโต

เขียน Unit Test แบบไหนก่อน จึงคุ้มที่สุดเมื่อโปรเจกต์เริ่มโต

เมื่อระบบเริ่มขยายตัว หลายทีมมักเริ่มหันมาให้ความสำคัญกับ Unit Test มากขึ้น แต่คำถามที่ตามมาทันทีคือ ควรเริ่มเขียน Test แบบไหนก่อน ถึงจะคุ้มที่สุด เพราะในโลกความจริง เวลาและทรัพยากรของทีมมีจำกัด การเลือกจุดที่ควรทดสอบก่อนจึงส่งผลโดยตรงต่อคุณภาพของระบบ

ประเด็นสำคัญคือ Unit Test ไม่ได้มีหน้าที่เหมือนกันทั้งหมด บางแบบช่วยป้องกันความพังหลังรีแฟกเตอร์ บางแบบช่วยจับข้อผิดพลาดจากข้อมูลขอบเขต บางแบบเก่งเรื่องป้องกันบั๊กที่ซ่อนอยู่ใน business rule ที่ซับซ้อน หากเลือกเขียนได้ถูกจุดตั้งแต่แรก สิ่งที่ทีมจะได้ไม่ใช่แค่ตัวเลข coverage ที่ดูดี แต่คือความมั่นใจเวลาต้องแก้โค้ดจริง

ทำความเข้าใจก่อนว่า Test แต่ละแบบมีหน้าที่ต่างกัน

การเขียน Unit Test อย่างมีประสิทธิภาพเริ่มจากการเข้าใจว่า Test แต่ละประเภทตอบโจทย์คนละปัญหา ไม่ใช่ทุก Test ที่ให้คุณค่าเท่ากันในทุกสถานการณ์ การแยกประเภทให้ชัดจะช่วยให้ทีมวางแผนได้ดีขึ้นว่าควรเริ่มจากอะไร

1) Happy Path Test: จุดเริ่มต้นที่คุ้มที่สุด

Happy Path Test คือการทดสอบเส้นทางการทำงานหลักของระบบ ว่ากรณีปกติที่ควรทำงานได้ ทำงานถูกต้องจริงหรือไม่

ตัวอย่างเช่น

  • ราคาสินค้า 100 บาท
  • ส่วนลด 10%
  • ผลลัพธ์ควรเป็น 90 บาท

Test ประเภทนี้มีประโยชน์มาก เพราะช่วยยืนยันว่า flow หลักของระบบยังทำงานได้ครบถ้วน และสามารถกันบั๊กพื้นฐานได้ดี สำหรับหลายระบบ นี่มักเป็นจุดเริ่มต้นที่คุ้มที่สุด เพราะหากเส้นทางหลักยังไม่รอด การไปไล่เขียนเคสซับซ้อนก็มักยังไม่คุ้ม

2) Edge Case Test: ตัวจับบั๊กบริเวณขอบเขต

Edge Case Test ใช้สำหรับตรวจสอบข้อมูลที่อยู่ตรงขอบของเงื่อนไข ซึ่งมักเป็นจุดที่บั๊กหลุดได้บ่อย

ตัวอย่างเช่น

  • จำนวนสินค้าเป็น 0
  • ส่วนลดเป็น 100%
  • ข้อความยาวเกิน limit
  • วันหมดอายุตรงกับเวลาเที่ยงคืน

หลายครั้งระบบดูเหมือนทำงานปกติในเคสทั่วไป แต่พังเมื่อเจอกับค่าริมขอบเหล่านี้ บั๊กประเภทนี้พบได้บ่อยกว่าที่คิด และมักส่งผลกระทบใน production อย่างเงียบ ๆ หากไม่ได้เตรียม Test ไว้

3) Negative Test: ตรวจความทนทานต่อข้อมูลผิดพลาด

Negative Test คือการทดสอบว่าระบบรับมือกับข้อมูลที่ไม่ถูกต้องได้ดีแค่ไหน ไม่ใช่แค่ตอบถูกในกรณีปกติ แต่ต้องไม่ล่มเมื่อรับ input ที่ผิดรูปแบบด้วย

ตัวอย่างเช่น

  • email ไม่ถูก format
  • id เป็นค่าว่าง
  • input เป็น null
  • ส่งค่าติดลบเข้าฟังก์ชันที่ไม่ควรรับค่าติดลบ

Test ประเภทนี้ช่วยลดความเสี่ยงของระบบล่ม ข้อมูลเสียหาย หรือพฤติกรรมที่คาดเดาไม่ได้ โดยเฉพาะในระบบที่ต้องรับข้อมูลจากผู้ใช้หรือจากระบบภายนอก

4) Branch Test: ครอบคลุมทุกเส้นทางของเงื่อนไข

เมื่อฟังก์ชันมี if-else หลายทาง การมี Test แค่เส้นทางเดียวอาจทำให้เข้าใจผิดว่าระบบปลอดภัย ทั้งที่จริงแล้วยังมีเงื่อนไขอีกหลายกิ่งที่ไม่เคยถูกตรวจสอบ

ตัวอย่างเช่น

  • ลูกค้า VIP ได้ราคาหนึ่งแบบ
  • ลูกค้าทั่วไปได้อีกแบบ
  • วันหยุดมีเงื่อนไขเพิ่ม

หาก Test ไม่ครอบคลุมทุก branch ที่สำคัญ บั๊กด้าน logic มักยังซ่อนอยู่ได้แม้ coverage จะดูดีบางส่วน Branch Test จึงเหมาะมากกับโค้ดที่มี business rule หลายชั้นและเปลี่ยนแปลงบ่อย

5) Regression Test: บั๊กที่เคยเกิด ต้องไม่กลับมาอีก

Regression Test คือ Test ที่เพิ่มขึ้นหลังจากพบปัญหาจริงในระบบ หลักคิดง่าย ๆ คือ บั๊กไหนเคยเกิด ต้องมี Test ล็อกไว้ เพื่อป้องกันไม่ให้มันย้อนกลับมาอีกในการ deploy รอบถัดไป

Test ประเภทนี้ถือว่าคุ้มค่ามาก เพราะสร้างจากประสบการณ์จริงของทีม ไม่ได้เดาเอาว่าอะไรอาจพัง แต่เป็นการป้องกันสิ่งที่เคยพังมาแล้วโดยตรง จึงเป็นหนึ่งใน Test ที่สร้างผลลัพธ์เชิงธุรกิจได้ชัดเจนที่สุด

6) Property-Based Thinking: มองหา “คุณสมบัติที่ต้องจริงเสมอ”

แม้ทีมจะไม่ได้ใช้เครื่องมือแนว property-based testing โดยตรง แต่สามารถยืมแนวคิดนี้มาใช้ได้ โดยเน้นทดสอบคุณสมบัติที่ควรเป็นจริงเสมอ ไม่ว่าจะเปลี่ยนรูปแบบข้อมูลอย่างไร

ตัวอย่างเช่น

  • ไม่ว่ารายการสินค้าจะเรียงลำดับแบบไหน ผลรวมราคาต้องเท่าเดิม
  • parse แล้ว serialize กลับ ค่าควรยังเท่าเดิม

แนวคิดนี้ช่วยจับบั๊กที่การยกตัวอย่างเคสธรรมดาอาจมองไม่เห็น โดยเฉพาะในระบบที่มีการคำนวณ การแปลงข้อมูล หรือมีความสัมพันธ์ของข้อมูลหลายมิติ

แล้วควรเริ่มเขียนอะไรก่อน

สำหรับงานส่วนใหญ่ ลำดับที่แนะนำคือ

  1. เขียน Happy Path ก่อน เพื่อให้ flow หลักทำงานได้แน่ชัด
  2. เติม Edge Case รอบค่าที่มีโอกาสพลาดสูง
  3. เติม Negative Test เพื่อกัน input ผิดรูปแบบ
  4. ไล่ Branch ที่เกี่ยวกับ business rule สำคัญ
  5. ทุกครั้งที่เจอ bug จริง ให้เพิ่ม Regression Test ทันที

ลำดับนี้ช่วยให้ทีมลงทุนกับ Test อย่างมีประสิทธิภาพ โดยเริ่มจากจุดที่ให้ผลตอบแทนสูงก่อน และค่อยขยายไปยังกรณีที่ซับซ้อนขึ้น

ถ้าทรัพยากรน้อยมาก ควรเริ่มจากตรงไหน

หากทีมมีเวลาและกำลังจำกัดมาก ควรเริ่มจากฟังก์ชันที่เข้าเงื่อนไข 3 ข้อนี้ก่อน

  • ฟังก์ชันคำนวณเงิน
  • ฟังก์ชันที่ตัดสินใจตามเงื่อนไขหลายชั้น
  • ฟังก์ชันที่แปลงข้อมูลเข้าออกระบบ

เหตุผลคือ 3 จุดนี้มักเป็นส่วนที่หากพังแล้วกระทบลูกค้าชัดเจน และบางครั้งยังพังแบบไม่แสดงอาการทันที ทำให้ตรวจจับยากและมีต้นทุนการแก้ไขสูง

อย่าเริ่มจาก Test ที่ผูกกับ implementation มากเกินไป

อีกเรื่องที่หลายทีมมองข้ามคือการเขียน Test ที่ยึดติดกับรายละเอียดภายในของ implementation มากเกินไป เช่น mock ทุกอย่างจนแน่นเกินจำเป็น

ผลเสียคือเมื่อมีการรีแฟกเตอร์เพียงเล็กน้อย Test อาจพังทั้งชุด ทั้งที่พฤติกรรมของระบบยังถูกต้องอยู่ แบบนี้จะทำให้ทีมเริ่มไม่เชื่อถือ Test และมองว่า Test เป็นภาระแทนที่จะเป็นเครื่องมือช่วยลดความเสี่ยง

Unit Test ที่คุ้มค่าจึงไม่ใช่ Test ที่เยอะที่สุด แต่คือ Test ที่

  • เตือนปัญหาได้เร็ว
  • ดูแลรักษาง่าย
  • ชี้สาเหตุของความผิดพลาดได้ชัด

ตัวอย่างฟังก์ชันที่ควรรีบมี Test

ฟังก์ชันลักษณะต่อไปนี้มักเป็นแหล่งบั๊กที่พบได้บ่อยในหลายระบบ

  • calculateDiscount()
  • validateEmail()
  • canUserCheckout()
  • parseConfig()

ฟังก์ชันเหล่านี้เกี่ยวข้องกับการคำนวณ การตรวจสอบข้อมูล การตัดสินใจเชิงเงื่อนไข และการแปลงข้อมูล ซึ่งล้วนเป็นพื้นที่เสี่ยงสูงในการเกิดข้อผิดพลาด

สรุป

การเขียน Unit Test ที่คุ้มค่า ไม่ได้หมายถึงการพยายามเขียนให้ครบทุกอย่างตั้งแต่วันแรก และไม่ได้หมายความว่า coverage 100% จะทำให้ระบบมีบั๊กน้อยเสมอไป สิ่งสำคัญกว่าคือการเลือกเขียนในจุดที่หากพังแล้วมีต้นทุนสูง ทั้งต่อผู้ใช้ ต่อธุรกิจ และต่อความมั่นใจของทีม

แนวทางที่ได้ผลสำหรับหลายทีมคือเริ่มจาก Happy Path เพื่อรักษา flow หลัก ต่อด้วย Edge Case และ Negative Test เพื่อป้องกันข้อมูลเสี่ยง จากนั้นค่อยไล่ Branch สำคัญ และปิดท้ายด้วย Regression Test ทุกครั้งที่เจอบั๊กจริง

ทีมที่เขียน Test เก่ง ไม่ใช่ทีมที่เขียนทุกอย่าง แต่คือทีมที่รู้ว่า อะไรควรถูกป้องกันก่อน เพราะพังแล้วแพงที่สุด