จุดเริ่มต้นของการเป็นโปรแกรมเมอร์ที่เหนือชั้นด้วย TDD
เชื่อไหมว่าครั้งหนึ่งผมเคยเป็นโปรแกรมเมอร์ที่เขียนโค้ดไปวันๆ โดยไม่มีแผนการอะไรเลย ผมมักจะเขียนฟังก์ชันเสร็จแล้วค่อยกดรันโปรแกรมเพื่อดูว่ามันพังตรงไหน ผลลัพธ์ที่ได้คือผมต้องมานั่งไล่แก้บั๊ก (Bug หรือข้อผิดพลาดในโปรแกรม) จนดึกดื่นเหมือนคนหลงทางในเขาวงกต การเขียนโค้ดแบบนั้นเปรียบเสมือนการสร้างยานอวกาศโดยไม่รู้วิธีบังคับทิศทาง มันเหนื่อยและท้อแท้จนเกือบถอดใจ
จนกระทั่งผมได้รู้จักกับ Test-Driven Development หรือที่เรียกสั้นๆ ว่า TDD ซึ่งเป็นแนวคิดการพัฒนาซอฟต์แวร์ที่ให้เรา เขียนชุดคำสั่งตรวจสอบผลลัพธ์ (Test) ก่อนเริ่มเขียนโค้ดจริง เปรียบเสมือนการมีเข็มทิศนำทางก่อนออกเดินทางไกล ทำให้เรามั่นใจได้ว่าทุกบรรทัดที่เราเขียนลงไปนั้นทำงานได้ถูกต้องจริงๆ ตั้งแต่วินาทีแรก
วงจรแห่งความสำเร็จ: แดง เขียว และปรับปรุง
หัวใจสำคัญของ TDD ไม่ใช่แค่เรื่องของเทคนิค แต่เป็นเรื่องของการปรับ Mindset (กระบวนการคิด) ให้เป็นระบบมากขึ้น เราจะทำงานวนลูปอยู่ 3 ขั้นตอนหลักที่เรียกว่า "Red-Green-Refactor" เริ่มจากการเขียน Test ให้มันพัง (Red) เขียนโค้ดให้มันผ่าน (Green) และปรับปรุงโค้ดให้สวยงาม (Refactor) โดยที่ Test ยังคงผ่านอยู่
การทำแบบนี้ช่วยให้เรามี Instant Feedback Loop หรือระบบแจ้งเตือนแบบทันทีทันใด เราไม่ต้องรอจนจบโปรเจกต์เพื่อมานั่งลุ้นว่าโค้ดจะพังหรือไม่ เพราะถ้าเราเผลอไปแก้ไขส่วนไหนจนทำให้ระบบพัง Test ที่เราเขียนไว้จะแจ้งเตือนเราทันที นี่คือ ตาข่ายนิรภัยชั้นดี ที่ทำให้เรากล้าที่จะปรับแต่งโค้ดให้ดียิ่งขึ้นโดยไม่ต้องกลัวว่าจะทำพัง
- Red: เขียน Test สำหรับฟังก์ชันที่ยังไม่มีอยู่จริง เพื่อให้แน่ใจว่ามันจะ "พัง" เพราะเรายังไม่ได้เขียนโค้ด
- Green: เขียนโค้ดเพียงเล็กน้อยเพื่อให้ Test นั้นผ่าน (Pass) ไม่ต้องสวย แต่ต้องทำงานได้ตามเงื่อนไข
- Refactor: ปรับปรุงโค้ดให้สะอาด (Clean Code) และมีประสิทธิภาพมากขึ้น โดยที่ Test ต้องไม่พัง
จากทฤษฎีสู่ความจริง: ลองเขียนโค้ดแบบ TDD
ลองมาดูตัวอย่างของการตรวจสอบสถานะผู้ใช้งานกัน สมมติว่าเราต้องการฟังก์ชันเช็คว่าผู้ใช้เป็นสมาชิกแบบ Premium หรือไม่ ถ้าเราเขียนโค้ดไปก่อนโดยไม่คิดถึงเคสที่ข้อมูลอาจจะว่างเปล่า เรามักจะเจอบั๊ก TypeError ในภายหลัง ซึ่งเป็นปัญหาที่พบบ่อยมากสำหรับมือใหม่ที่เพิ่งเริ่มหัดเขียนโปรแกรม
การใช้ TDD จะบังคับให้เราคิดถึงสถานการณ์ต่างๆ ล่วงหน้า เช่น "ถ้าผู้ใช้ไม่มีข้อมูลการสมัครสมาชิกเลย โปรแกรมควรจะทำอย่างไร?" นี่คือตัวอย่างการเขียน Test ก่อนเริ่มเขียนฟังก์ชันจริงครับ
// ไฟล์ premiumAccess.test.js
// เรากำหนดความคาดหวังไว้ก่อนว่า ถ้า user ไม่มี subscription ต้องคืนค่า false
test('should return false if subscription is missing', () => {
const user = { name: 'Somchai' };
expect(canAccessPremium(user)).toBe(false);
});
// ไฟล์ premiumAccess.js
function canAccessPremium(user) {
// เขียนโค้ดดักไว้ก่อนว่าถ้าไม่มี subscription ให้รีบจบการทำงาน
if (!user.subscription) return false;
return user.subscription.tier === 'premium';
}
จากตัวอย่างนี้ คุณจะเห็นว่าเราไม่ได้เขียนโค้ดฟุ่มเฟือย แต่เขียนแค่ส่วนที่จำเป็นเพื่อให้ Test ผ่านเท่านั้น หากวันหนึ่งมีคนมาเปลี่ยนโครงสร้างข้อมูลในฐานข้อมูล Test เหล่านี้จะตะโกนบอกเราทันทีว่าโค้ดส่วนไหนที่ต้องแก้ไข ทำให้เราประหยัดเวลาในการเดาสาเหตุของบั๊กไปได้มหาศาลเลยครับ
สรุป: ก้าวแรกสู่การเป็นมืออาชีพ
การฝึก TDD อาจจะดูเหมือนทำให้งานช้าลงในช่วงแรก เพราะคุณต้องใช้เวลาเขียน Test เพิ่มขึ้น แต่เชื่อผมเถอะครับว่าในระยะยาวมันคือการลงทุนที่คุ้มค่าที่สุด มันช่วยลดเวลาการนั่งแก้บั๊กในช่วงท้ายโปรเจกต์ และทำให้คุณเขียนโค้ดด้วยความมั่นใจเหมือนเจไดที่ฝึกฝนวิชาดาบจนชำนาญ ก่อนจะออกไปทำภารกิจจริงในสนามรบ
สำหรับน้องๆ ที่กำลังเริ่มต้น แนะนำให้ลองใช้ TDD กับโจทย์เล็กๆ ในโปรเจกต์ของตัวเองดูครับ เริ่มจากฟังก์ชันคำนวณเลขง่ายๆ หรือการตรวจสอบค่า input แล้วคุณจะพบว่า ความกลัวการเขียนโค้ดจะหายไป เพราะคุณมี "เครื่องมือพิสูจน์" อยู่ในมือเสมอ หากวันไหนที่คุณรู้สึกว่าโปรแกรมที่เขียนอยู่เริ่มควบคุมไม่ได้ นั่นแหละครับคือสัญญาณว่าถึงเวลาต้องนำ TDD มาใช้แล้ว
ที่มา: Test-Driven Development: My Jedi Training — DEV Community