Kotchasan PHP Framework

การทดสอบโค้ดและปัญหาที่เกิดจากการทำ 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 ที่มากเกินไป
  • เลือกใช้เครื่องมือทดสอบที่เหมาะสมและช่วยให้การทดสอบง่ายขึ้น
  • เขียนชุดทดสอบที่ครอบคลุมทุกกรณีและสามารถปรับแก้ไขได้เมื่อโค้ดมีการเปลี่ยนแปลง

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