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

Table-Driven Test เทคนิคเขียน Unit Test ให้สั้น กระชับ และเพิ่มเคสง่าย
การเขียน Unit Test แบบแยกทีละเคสอาจเริ่มต้นได้ง่าย แต่เมื่อจำนวนกรณีทดสอบเพิ่มขึ้น ไฟล์ทดสอบก็มักยาวขึ้น ซ้ำขึ้น และดูแลได้ยากขึ้นตามไปด้วย ปัญหานี้เกิดขึ้นกับหลายทีม โดยเฉพาะในงานที่มีรูปแบบการทดสอบคล้ายกัน แต่เปลี่ยนเพียงค่า input และผลลัพธ์ที่คาดหวัง
Table-Driven Test เป็นเทคนิคที่ช่วยแก้ปัญหานี้ได้อย่างตรงจุด โดยนำข้อมูลทดสอบทั้งหมดมารวมไว้ในโครงสร้างเดียว เช่น ตาราง รายการ หรือชุดข้อมูล จากนั้นให้ตัวทดสอบหนึ่งชุดวนรันทดสอบทุกกรณีโดยอัตโนมัติ ผลลัพธ์คือโค้ดสั้นลง อ่านง่ายขึ้น และเพิ่มเคสใหม่ได้สะดวกมาก
Table-Driven Test คืออะไร
แนวคิดหลักของ Table-Driven Test คือการแยก “ข้อมูลทดสอบ” ออกจาก “ตรรกะของการทดสอบ” อย่างชัดเจน
- ข้อมูลทดสอบมีหน้าที่ระบุว่าใส่อะไรเข้าไป
- ผลลัพธ์ที่คาดหวังมีหน้าที่บอกว่าควรได้อะไรออกมา
- ตัว test มีหน้าที่วนลูป รันฟังก์ชัน และเปรียบเทียบผลเท่านั้น
แทนที่จะเขียน test 5 หรือ 10 อันที่มีโครงเหมือนกันทั้งหมด เราสามารถเขียน test เพียงชุดเดียว แล้วให้ข้อมูลในตารางเป็นตัวกำหนดว่าต้องทดสอบอะไรบ้าง
จุดเด่นที่ทำให้หลายทีมเลือกใช้
ข้อดีของ Table-Driven Test มีหลายด้านและเห็นผลได้ค่อนข้างเร็ว
- ลดโค้ดซ้ำที่เกิดจากการเขียน test รูปแบบเดิมหลายครั้ง
- เพิ่มกรณีทดสอบใหม่ได้ง่าย เพียงเพิ่มแถวข้อมูลเข้าไป
- มองเห็นภาพรวมของเคสทั้งหมดได้ทันทีในบล็อกเดียว
- ลดโอกาสลืมเคสสำคัญ โดยเฉพาะเคสขอบหรือ edge case
- ช่วยให้ reviewer ตรวจสอบความครอบคลุมของ test ได้เร็วขึ้น
เมื่อโค้ดทดสอบมีโครงสร้างที่เป็นระบบมากขึ้น ทีมก็จะขยายชุดทดสอบได้ง่ายขึ้นตามไปด้วย และนั่นมีผลโดยตรงต่อคุณภาพของซอฟต์แวร์
เหมาะกับงานประเภทไหน
Table-Driven Test เหมาะมากกับงานที่มีความสัมพันธ์ระหว่าง input และ output ชัดเจน เช่น
- การคำนวณราคา
- การตรวจสอบสิทธิ์
- การจัดรูปแบบข้อความ
- การตรวจสอบข้อมูลในฟอร์ม
- การแปลงข้อมูลจาก API
งานลักษณะนี้มักมีเงื่อนไขย่อยหลายแบบ แต่รูปแบบของการทดสอบคล้ายกันมาก จึงเหมาะกับการรวมเป็นตารางเพื่อให้จัดการได้ง่ายกว่าเดิม
ตัวอย่างแนวคิดแบบง่าย
สมมติว่ามีฟังก์ชันสำหรับบวกเลข หากเขียนแบบเดิม อาจต้องสร้าง test แยกสำหรับการบวกเลขปกติ บวกเลขศูนย์ บวกเลขติดลบ หรือค่าที่มากผิดปกติ
แต่ถ้าใช้ Table-Driven Test เราสามารถสร้างตารางที่เก็บข้อมูลเช่น
- ค่า a
- ค่า b
- ผลลัพธ์ที่คาดหวัง
- ชื่อกรณีทดสอบ
จากนั้นใช้ test ตัวเดียววนทดสอบทุกแถวในตาราง ผลคือจำนวนบรรทัดลดลง แต่จำนวนเคสเพิ่มขึ้นได้โดยไม่ทำให้ไฟล์รก
เคล็ดลับสำคัญ: ตั้งชื่อแต่ละเคสให้ชัด
หนึ่งในเทคนิคที่ช่วยให้ Table-Driven Test ใช้งานได้ดีขึ้นมาก คือการตั้งชื่อให้แต่ละกรณีทดสอบ เช่น
- บวกเลขปกติ
- บวกเลขติดลบ
- บวกเลขศูนย์
- ค่ามากผิดปกติ
ประโยชน์ของการตั้งชื่อเคสคือ เมื่อทดสอบแล้วเกิดความล้มเหลว ระบบจะแสดงได้ทันทีว่าเคสใดมีปัญหา ทำให้นักพัฒนาไม่ต้องเสียเวลาไล่อ่านทั้งไฟล์เพื่อหาต้นตอของข้อผิดพลาด
ใช้ได้กับหลายภาษา ไม่ได้จำกัดแค่ Go
แม้ว่าแนวทางนี้จะได้รับความนิยมมากในภาษา Go เพราะสามารถใช้ slice ของ test case ได้อย่างกระชับและอ่านง่าย แต่จริง ๆ แล้วแนวคิดเดียวกันสามารถนำไปใช้ได้กับหลายภาษา
- JavaScript สามารถใช้ array ของเคส และวนด้วย
forEach - Python สามารถใช้ list of dicts
- Java สามารถใช้ parameterized test
- ภาษาอื่น ๆ ก็ประยุกต์ใช้ได้ในรูปแบบคล้ายกัน
ดังนั้น Table-Driven Test ไม่ใช่เทคนิคเฉพาะภาษา แต่เป็นรูปแบบความคิดในการออกแบบ test ให้เป็นระบบมากขึ้น
พลังของการใส่ Edge Case ตั้งแต่แรก
สิ่งที่ทำให้ Table-Driven Test มีประโยชน์มากขึ้นอีกระดับ คือการบังคับให้เราคิดถึง edge case อย่างเป็นระบบตั้งแต่ต้น เช่น
- ค่าว่าง
- ค่ายาวผิดปกติ
- ข้อมูล null
- ตัวอักษรพิเศษ
- ค่าที่เกินขอบเขต
ในโลกจริง บั๊กจำนวนมากไม่ได้เกิดจากกรณีใช้งานปกติ แต่เกิดจากสถานการณ์มุม ๆ ที่ไม่มีใครลองก่อน deploy การจัด edge case ไว้ในตารางทดสอบตั้งแต่แรก จะช่วยลดโอกาสที่ปัญหาเหล่านี้หลุดไปถึง production
ทำไมคนรีวิวโค้ดถึงชอบแนวทางนี้
สำหรับการรีวิวโค้ด Table-Driven Test มีข้อได้เปรียบชัดเจน เพราะ reviewer สามารถเปิดมาแล้วเห็นรายการกรณีทดสอบทั้งหมดในจุดเดียว ทำให้ตรวจสอบได้รวดเร็วว่า
- มีเคสหลักครบหรือไม่
- มี edge case ที่สำคัญแล้วหรือยัง
- มีกรณีที่ควรเพิ่มอีกหรือเปล่า
การสื่อสารระหว่างคนเขียนกับคนรีวิวจึงง่ายขึ้น และช่วยยกระดับมาตรฐานของชุดทดสอบในทีมได้ดี
วิธีเขียนให้สะอาดและดูแลง่าย
หากต้องการให้ Table-Driven Test อ่านง่ายจริง ควรแยกบทบาทของแต่ละส่วนให้ชัดเจน
- ส่วนข้อมูลทดสอบ ระบุ input และ expected output
- ส่วน logic ของ test ทำหน้าที่รันฟังก์ชันและตรวจผล
- หากมีขั้นตอนเตรียมข้อมูลที่ซับซ้อน ควรแยก helper function ออกมา
แนวทางนี้จะช่วยให้ไฟล์ทดสอบสะอาดขึ้น และลดความสับสนเวลาต้องกลับมาแก้ไขในอนาคต
ข้อควรระวังในการใช้งาน
แม้ Table-Driven Test จะมีข้อดีมาก แต่ก็มีสิ่งที่ควรระวังเช่นกัน คือไม่ควรพยายามยัดทุกอย่างลงในตารางจนอ่านยากเกินไป
ถ้าการเตรียมข้อมูลเริ่มซับซ้อนมาก หรือแต่ละกรณีมีเงื่อนไขเฉพาะแตกต่างกันมาก อาจต้องใช้ helper function หรือแยก test บางส่วนออกไป เพื่อคงความชัดเจนในการอ่าน
เป้าหมายของเทคนิคนี้ไม่ใช่แค่ลดจำนวนบรรทัด แต่คือการทำให้ test เข้าใจง่าย ขยายต่อได้ง่าย และยังคงสื่อความหมายได้ดี
เมื่อไรควรเริ่มใช้ Table-Driven Test
สัญญาณที่ชัดที่สุดคือ เมื่อ test ของคุณเริ่มมีรูปแบบซ้ำกันหลายครั้ง และสิ่งที่เปลี่ยนมีเพียง input กับ expected output เท่านั้น นั่นคือช่วงเวลาที่เหมาะมากในการเปลี่ยนมาใช้แนวคิดแบบ table-driven
ยิ่งระบบมีขนาดใหญ่ขึ้น เทคนิคเล็ก ๆ แบบนี้จะยิ่งให้ผลลัพธ์ที่ชัดเจน จาก test ที่เคยยาว กระจัดกระจาย และขยายยาก จะกลายเป็นชุดทดสอบที่สั้น กระชับ และจัดการได้เป็นระบบมากขึ้น
สรุป
Table-Driven Test เป็นเทคนิคที่เรียบง่ายแต่ทรงพลังสำหรับการเขียน Unit Test ช่วยลดโค้ดซ้ำ เพิ่มจำนวนเคสได้สะดวก มองเห็นภาพรวมของการทดสอบได้ดี และลดโอกาสที่บั๊กจากกรณีมุม ๆ จะหลุดไปถึง production
สำหรับมือใหม่ เทคนิคนี้คุ้มค่าต่อการฝึกมาก เพราะช่วยสร้างนิสัยการคิดเคสอย่างเป็นระบบ และทำให้การเขียน test ไม่น่าเบื่อเหมือนเดิม ส่วนสำหรับทีมที่ดูแลระบบขนาดใหญ่ แนวทางนี้ยิ่งช่วยให้การบำรุงรักษา test มีประสิทธิภาพและขยายต่อได้ง่ายในระยะยาว