การทดสอบโค้ดและปัญหาที่เกิดจากการทำ Test ยาก พร้อมแนวทางปรับปรุง
การทำ Test เป็นขั้นตอนสำคัญในการพัฒนาโปรแกรม เพราะช่วยให้เราสามารถตรวจสอบและหาข้อผิดพลาดของโค้ดได้ตั้งแต่เนิ่นๆ ก่อนที่จะนำไปใช้งานจริง
การทดสอบไม่เพียงแต่ช่วยให้มั่นใจว่าโค้ดทำงานตามที่คาดหวังแล้ว แต่ยังช่วยให้การพัฒนาระบบมีความเสถียรและสามารถแก้ไขปัญหาได้ง่ายในอนาคต
1. ปัญหาที่พบในการทำ Test
-
ความซับซ้อนของโค้ด
เมื่อโค้ดถูกเขียนในลักษณะที่มีความเชื่อมโยงกันมาก (tightly coupled) การแยกส่วนเพื่อนำมาทดสอบแต่ละส่วน (unit testing) จึงเป็นเรื่องยาก
ตัวอย่างเช่น เมื่อมีการเรียกใช้บริการจากระบบภายนอกหรือฐานข้อมูลโดยตรง ทำให้การสร้าง mock หรือ stub เพื่อเลียนแบบการทำงานของส่วนเหล่านั้นเป็นเรื่องท้าทาย -
การขึ้นต่อกับ dependency
โค้ดที่ขึ้นกับ dependency จำนวนมาก ทำให้ต้องมีการจัดการหรือแก้ไข dependency เหล่านั้นก่อนที่จะทำการทดสอบ
การทดสอบจึงต้องคำนึงถึงการ setup environment ที่จำเป็นหรือการ mock dependency เหล่านั้นให้เหมาะสม -
เวลาในการรันการทดสอบ
เมื่อมีการทดสอบร่วมกัน (integration test) หลายฟังก์ชั่นที่เชื่อมต่อกัน การรัน test ทั้งหมดอาจใช้เวลานาน
ซึ่งส่งผลให้การ feedback เกี่ยวกับความถูกต้องของระบบเกิดขึ้นช้า -
การจัดการกับผลลัพธ์ที่ไม่แน่นอน
บางครั้งผลลัพธ์ที่ได้จากการทดสอบอาจมีความแปรปรวน เช่น ขึ้นอยู่กับเวลา หรือการตอบสนองของระบบภายนอก
ทำให้ต้องมีการออกแบบการทดสอบที่สามารถรองรับสถานการณ์เหล่านี้ได้
2. แนวทางการปรับปรุงและพัฒนาการทดสอบ
-
ออกแบบโค้ดให้มีความเป็น modular
การแยกโค้ดออกเป็นโมดูลย่อยๆ ช่วยให้แต่ละส่วนสามารถทดสอบได้อย่างอิสระ
นอกจากนี้ยังทำให้สามารถปรับปรุงหรือเปลี่ยนแปลงส่วนใดส่วนหนึ่งโดยไม่ส่งผลกระทบต่อส่วนอื่น ๆ -
ลด dependency ที่ไม่จำเป็น
พยายามหลีกเลี่ยงการเขียนโค้ดที่ขึ้นต่อกันมากเกินไป
ใช้ dependency injection หรือ design pattern ที่ช่วยให้แยกการทำงานของส่วนต่าง ๆ ได้ง่ายขึ้น -
ใช้เครื่องมือและ framework ที่เหมาะสม
เช่น การใช้ Jest, Mocha หรือ Jasmine สำหรับการทดสอบในภาษา JavaScript
โดยเครื่องมือเหล่านี้จะช่วยให้การสร้าง mock, stub และ spy เป็นไปได้อย่างง่ายดาย
นอกจากนี้ยังช่วยจัดการกับ asynchronous test ได้อย่างมีประสิทธิภาพ -
ออกแบบชุดทดสอบให้ครอบคลุมและสามารถปรับปรุงได้ง่าย
ควรเขียน test case ที่ครอบคลุมทุกกรณีที่เป็นไปได้ รวมถึงกรณี edge case
และควรมีการจัดการกับผลลัพธ์ที่อาจเปลี่ยนแปลงไปตามสถานการณ์
3. ตัวอย่างโค้ดสำหรับการทดสอบด้วย Jest ในภาษา JavaScript
/*
ฟังก์ชั่น calculateSum ทำหน้าที่บวกค่าของตัวเลขในอาร์เรย์
@param {Array<number>} numbers - อาร์เรย์ของตัวเลขที่ต้องการบวก
@return {number} ผลรวมของตัวเลขในอาร์เรย์
*/
function calculateSum(numbers) {
return numbers.reduce((sum, number) => sum + number, 0);
}
/*
ตัวอย่างการทดสอบฟังก์ชั่น calculateSum โดยใช้ Jest
@description ทดสอบว่า calculateSum คืนค่าผลรวมที่ถูกต้องหรือไม่
*/
describe('calculateSum', () => {
test('ควรคืนผลรวมของตัวเลขในอาร์เรย์', () => {
/*
ทดสอบโดยใช้ข้อมูลชุดแรก
คาดหวังให้ผลลัพธ์คือ 6 เมื่อใส่อาร์เรย์ [1, 2, 3]
*/
expect(calculateSum([1, 2, 3])).toBe(6);
});
test('ควรคืนค่า 0 เมื่ออาร์เรย์ว่าง', () => {
/*
ทดสอบโดยใช้ข้อมูลชุดที่สอง
คาดหวังให้ผลลัพธ์คือ 0 เมื่อใส่อาร์เรย์ที่ว่างเปล่า
*/
expect(calculateSum([])).toBe(0);
});
});
4. สรุปแนวทางปรับปรุงการทำ Test
- ควรออกแบบและเขียนโค้ดในลักษณะที่แยกส่วนได้ชัดเจน
- ลดการพึ่งพา dependency ที่มากเกินไป
- เลือกใช้เครื่องมือทดสอบที่เหมาะสมและช่วยให้การทดสอบง่ายขึ้น
- เขียนชุดทดสอบที่ครอบคลุมทุกกรณีและสามารถปรับแก้ไขได้เมื่อโค้ดมีการเปลี่ยนแปลง
การปรับปรุงกระบวนการทดสอบในด้านเหล่านี้จะช่วยให้การพัฒนาระบบมีประสิทธิภาพมากขึ้น
ทั้งยังทำให้เราสามารถตรวจจับข้อผิดพลาดได้เร็วและแก้ไขปัญหาก่อนที่จะส่งผลกระทบต่อผู้ใช้งานจริง