ในการพัฒนาซอฟต์แวร์แบบดั้งเดิม เรามักจะเขียนโค้ดจนเสร็จก่อนแล้วค่อยเขียน Unit Test ตามหลัง ( หรือบางครั้งก็ข้ามไปเลย ) แต่ Test-Driven Development ( TDD ) กลับด้านกระบวนการนั้นโดยสิ้นเชิง TDD คือแนวคิดที่ว่า “เราต้องเขียนแบบทดสอบ ( Test ) ก่อนเริ่มเขียนฟีเจอร์จริง”
วัฏจักร Red-Green-Refactor
หัวใจสำคัญของ TDD ประกอบด้วย 3 ขั้นตอนสั้น ๆ ที่วนซ้ำไปเรื่อย ๆ จนกว่าจะได้ซอฟต์แวร์ที่สมบูรณ์
- 🔴 Red ( เขียน Test ให้พัง ): เขียนแบบทดสอบสำหรับฟีเจอร์ใหม่ที่คุณต้องการ โดยที่ยังไม่มีโค้ดจริงรองรับ เมื่อรัน Test ผลลัพธ์จะต้อง “ไม่ผ่าน” ( Fail ) เสมอ เพื่อยืนยันว่า Test นั้นทำงานได้จริงและตรวจพบจุดที่ยังไม่มีโค้ด
- 🟢 Green ( เขียนโค้ดให้ผ่าน ): เขียนโค้ดจริงด้วยวิธีที่เรียบง่ายที่สุด ( แม้โค้ดอาจจะไม่สวยงามหรือ Hard-code ไว้ก่อน ) เป้าหมายเดียวในขั้นนี้คือทำให้ Test ที่เพิ่งเขียนไปเปลี่ยนสถานะเป็น “ผ่าน” ( Pass )
- 🔵 Refactor ( ปรับปรุงโค้ด ): เมื่อโค้ดทำงานถูกต้องแล้ว เราจะกลับมาปรับแต่งโครงสร้างโค้ด ( Refactoring ) ให้สะอาด อ่านง่าย และมีประสิทธิภาพมากขึ้น โดยที่มี Test เป็น “ตาข่ายนิรภัย” ( Safety Net ) คอยเช็คว่าการแก้ไขของเราไม่ได้ไปทำให้อะไรพัง
ทำไมเราถึงควรใช้ TDD ?
- ความมั่นใจในการแก้ไข ( High Confidence ): เมื่อคุณมี Test ครอบคลุมทุกส่วน คุณจะกล้าแก้ไขโค้ดเก่าหรือเพิ่มฟีเจอร์ใหม่ เพราะถ้าคุณพลาด Test จะเตือนคุณทันที
- ลดข้อผิดพลาด ( Fewer Bugs ): เนื่องจากการเขียน Test ก่อนบังคับให้คุณต้องคิดถึง Error หรือ Edge Case ต่างๆ ล่วงหน้า ทำให้โอกาสที่ Bug จะหลุดไปถึง Production น้อยลงมาก
- โค้ดมีคุณภาพดีขึ้น ( Better Design ): การเขียน Test ก่อนบังคับให้โค้ดของคุณต้อง “Testable” ซึ่งมักจะนำไปสู่การออกแบบที่เป็น Modular แยกส่วนชัดเจน และไม่ผูกติดกันจนเกินไป ( Loosely Coupled )
- เป็นเอกสารที่มีชีวิต ( Living Documentation ): โค้ดของ Test จะทำหน้าที่บอกนักพัฒนาคนอื่น ( หรือตัวคุณเองในอนาคต ) ว่าฟังก์ชันนี้ทำงานอย่างไรและคาดหวังผลลัพธ์แบบไหน
ข้อควรระวังและการเริ่มต้น
แม้ TDD จะดูดีในทฤษฎี แต่ในทางปฏิบัติมีสิ่งที่ต้องพิจารณา
- ใช้เวลาในช่วงแรกนาน: การเขียน Test ก่อนอาจทำให้รู้สึกว่างานเดินช้าลงในช่วงเริ่มต้น
- ต้องอาศัยวินัย: เป็นเรื่องง่ายมากที่จะกลับไปเขียนโค้ดก่อนแล้วค่อยเขียน Test ตามหลัง
คำแนะนำสำหรับการเริ่มต้น
เริ่มจากโปรเจกต์เล็ก ๆ หรือฟังก์ชันที่มี Logic ซับซ้อน เพื่อให้เห็นประโยชน์ของการมี Test คอยตรวจสอบความถูกต้อง แล้วคุณจะพบว่าเวลาที่เสียไปกับการเขียน Test นั้น คุ้มค่ากว่าการต้องมานั่งแก้ Bug มหาศาลในภายหลังครับ
อ่านเพิ่มเติม