ในการเขียนโปรแกรม หลายคนอาจเคยเจอเหตุการณ์ “แก้จุดหนึ่ง พังอีกจุดหนึ่ง” หรือที่เรียกว่า Regression การเขียน Unit Test จึงเปรียบเสมือนการสร้าง “เกราะป้องกัน” ให้กับ Code ของเรา เพื่อให้มั่นใจว่าฟังก์ชันเล็ก ๆ แต่ละส่วนทำงานได้ถูกต้องตามที่ตั้งใจไว้
Unit Test คืออะไร ?
Unit Test คือการทดสอบซอฟต์แวร์ในระดับที่เล็กที่สุด ( Smallest unit ) ซึ่งโดยปกติหมายถึงการทดสอบ “ฟังก์ชัน” ( Function ) หรือ “เมธอด” ( Method ) เพียงอย่างเดียว โดยไม่สนใจส่วนประกอบภายนอก เช่น Database หรือ API อื่น ๆ
หลักการสำคัญของ Unit Test ( FIRST Rule )
- Fast: ต้องรันได้เร็วมาก ( หลักมิลลิวินาที ) เพื่อให้รันได้บ่อย ๆ
- Independent: เทสแต่ละตัวต้องไม่ขึ้นต่อกัน ลำดับการรันต้องไม่มีผล
- Repeatable: รันกี่ครั้ง ผลลัพธ์ต้องเหมือนเดิมเสมอ
- Self-Validating: ต้องบอกผลลัพธ์ได้ชัดเจนว่า “ผ่าน ( Pass )” หรือ “ไม่ผ่าน ( Fail )” โดยไม่ต้องให้คนมาตีความ
- Thorough/Timely: ครอบคลุมกรณีต่างๆ และควรเขียนพร้อม ๆ กับการเขียน Code
โครงสร้างของ Unit Test ( AAA Pattern )
เพื่อให้ Unit Test อ่านง่ายและเป็นระเบียบ เรานิยมใช้โครงสร้าง AAA
- Arrange: การเตรียมข้อมูลและสภาพแวดล้อมที่จำเป็นสำหรับบททดสอบ
- Act: การเรียกใช้งานฟังก์ชันหรือเมธอดที่ต้องการทดสอบจริง
- Assert: การตรวจสอบว่าผลลัพธ์ที่ได้ ( Actual ) ตรงกับที่คาดหวังไว้ ( Expected ) หรือไม่
ทำไมเราต้องเขียน Unit Test ?
- ลด Bug ในระยะยาว: การเจอข้อผิดพลาดตั้งแต่ตอนเขียน Code แก้ไขได้ง่ายและราคาถูกกว่าการไปเจอตอน Deploy แล้ว
- กล้าที่จะ Refactor: เมื่อเรามี Unit Test ครอบคลุม เราจะกล้าปรับปรุง Code ให้สะอาดขึ้น เพราะถ้าระบบพัง เทสจะแจ้งเตือนเราทันที
- เป็น Documentation ไปในตัว: Code ของ Unit Test จะบอกเราว่าฟังก์ชันนี้ควรรับค่าอะไร และได้ผลลัพธ์แบบไหน
- ประหยัดเวลา Manual Test: ไม่ต้องคอยกดรันโปรแกรมหน้าจอเดิม ๆ ซ้ำ ๆ เพื่อเช็คว่าพังไหม
ตัวอย่างการเขียน Unit Test ( Pseudo Code )
สมมติเรามีฟังก์ชันบวกเลขง่าย ๆ
// ฟังก์ชันที่ต้องการทดสอบ
function add(a, b) {
return a + b;
}
// Unit Test
test('ควรคืนค่าเป็น 5 เมื่อบวก 2 กับ 3', () => {
// 1. Arrange
const num1 = 2;
const num2 = 3;
const expected = 5;
// 2. Act
const result = add(num1, num2);
// 3. Assert
expect(result).toBe(expected);
});
ข้อแนะนำสำหรับมือใหม่
- อย่าทดสอบ Logic ที่ซับซ้อนเกินไปในหนึ่งเทส: หนึ่งเทสควรตรวจสอบเพียง “เรื่องเดียว”
- ใช้ Mocking: หากฟังก์ชันของคุณต้องไปดึงข้อมูลจาก Database ให้ใช้เครื่องมือจำลอง ( Mock ) ข้อมูลขึ้นมาแทน เพื่อรักษาความเร็วและความเป็นอิสระของ Unit Test
- เริ่มจากเส้นทางที่ถูกต้อง ( Happy Path ): เขียนเทสให้เคสที่ทำงานปกติผ่านก่อน แล้วค่อยเขียนดักเคสที่น่าจะพัง ( Edge Cases )
สรุป
การเขียน Unit Test อาจดูเหมือนเสียเวลาในช่วงแรก แต่ในโปรเจกต์ที่มีความซับซ้อนหรือต้องดูแลรักษาในระยะยาว มันคือการลงทุนที่คุ้มค่าที่สุดอย่างหนึ่งของเหล่านักพัฒนาครับ
อ่านเพิ่มเติม
