TDD Misconception
บทความนี้เป็นความเห็นล้วนๆ ไม่ได้อ้างอิงอะไรนอกจากประสบการณ์และความเห็นส่วนตัว
จากที่ผมลองเริ่มทำ TDD และลองถามตัวเองรวมถึงย้อนนึกไปถึงว่าทำไมทีมเก่าๆ ถึงไม่ได้ทำ TDD หรือลองทำแล้วล้มเลิก มันเกิดจากอะไรบ้าง ผมก็เลยรวบรวมเท่าที่นึกออกมาเผื่อใครที่เจอปัญหาแบบเดียวกันจะได้ลองอ่านดูให้เป็นประโยชน์
TDD ไม่ได้ทำให้ลด Bug
นี่เป็น Misconception ใหญ่อันนึงเลย คือ TDD ไม่ได้มีไว้ทำให้ Bug น้อยลง แต่มันมีไว้ทำให้เรามี Feedback loop ที่เร็วขึ้นและทำให้เราไม่ทำ Bug ซ้ำ
ตอนที่ผมเริ่มทำ TDD ผมเข้าใจตรงนี้ผิดไปมาก รวมถึง Management เองก็เข้าใจผิดว่าถ้ามีสิ่งนี้แล้วคุณควรจะทำ Bug น้อยลงตั้งแต่แรก ก็คิดเทสให้มันครอบคลุมสิ คิดให้มันเยอะๆ ก่อนเขียนเทสสิ
ซึ่งนั่นแหละขัดกับหลัก Agile และความรวดเร็วทุกประการ
ตอนนี้สิ่งที่ผมทำคือ เขียนเทสให้โง่และง่ายที่สุด เขียนโค้ดให้ผ่านเทส แล้วถ้ามันผ่านก็เลิก ไปทำอย่างอื่นต่อ ถ้าไม่ได้เป็นประเด็นที่มันใหญ่ๆ ที่ต้องการความชัวร์มากๆ ผมจะทำแค่นี้
พอมี Bug จาก QA หรือพอเราลองกดแล้วเจอ Bug เมื่อนั้นผมก็จะ Add test และ Add production code เข้าไป
ซึ่งพอย้อนไปย้อนมาตอนนี้ถึงได้เข้าใจว่าทำไม Kent Beck ผู้ก่อตั้ง TDD ถึงบอกว่า Make it work, make it right, make it fast. และเจ้าตัวเองก็บอกว่า Write test just enough to make a sleep at night. และทำไมถึงบอกว่า Release often
เพราะเมื่อคุณทำ TDD สิ่งที่คุณได้ไม่ใช่ Perfect software แต่เป็น Software มี Bug เหมือนเดิมนั่นแหละ แต่ใช้เวลาในการแก้ Bug สั้นลงมากๆ นั่นแหละคือความหมายลึกๆ ที่ว่า TDD ควร Release often
ข้อผิดพลาดแรก: พยายามเขียนเทสให้ครอบคลุมเกินไป
อย่าพยายามบอกให้เพื่อนร่วมทีมว่าคิดนานๆ คิดดีๆ ก่อนเขียนเทส เขียนให้เยอะให้ครอบคลุม เขียนให้ Perfect ฝั่งบริหารก็อย่าบอกว่าทำเทสแล้วทำไมไม่ทำให้ครอบคลุมให้มันดีๆ วิธีคิดและ Attitude แบบนี้นั่นแหละที่ทำให้เรารู้สึกว่าเขียนเทสมันเสียเวลา
เราเขียน Test เพื่อลด Mental workload จากความกลัวที่จะเขียนโค้ด (กลัวพัง) แต่ถ้าเราไปสร้างความกลัวที่จะเขียนเทสไม่ครอบคลุมแต่แรก นี่มันไม่ได้อะไรขึ้นมาเลยนะ
Make it work, before make it right
ทำการทดสอบมากเกินไปใน 1 เคส
ผมมีหลักส่วนตัวแล้วว่า Test ต้องคิดได้ใน 2 นาที และเขียนเสร็จภายใน 10 นาที แต่โดยเฉลี่ยผมจะเสียเวลาต่อเทสไม่เกิน 5 นาทีเสมอ
ถ้าเกินกว่านั้นผมจะเริ่มถามตัวเองแล้วว่า เราเทสเยอะไปมั้ย
เช่น ผมทำตัว Aggregrate ข้อมูล เคสแรกที่ผมจะเขียน
It should aggregate correctly
สำหรับคำว่า Correctly ที่นี้ก็จะเป็นอะไรที่โง่ๆ ที่สุด
assert(aggregrate([
{ title: ‘Data1’, value: 3},
{ title: ‘Data2’, value:4 }
]).equal(7)
แล้วก็เริ่ม Production code เลย
พอทำเสร็จเราเจอปัญหาว่า เห้ย บางที Field ไม่ได้ชื่อ Value นะ เราก็มาเพิ่มเทสเคสใหม่ แล้วเพิ่ม Production code ใหม่
พอเจอปัญหาว่าบางทีส่งค่าไปในฟังก์ชั่นเป็น Null แล้วฟังก์ชั่นพัง เกิดเป็น Bug ขึ้นมาเราก็เพิ่ม
It should return 0 when get null
นั่นแหละ ผมเจอปัญหาก่อนแล้วค่อยเพิ่มเทสเคสไปทีละเคส ไม่ใช่พยายามเขียนเทสให้ครอบคลุมทุกปัญหา เทสทีละอย่าง ทีละปัญหา
Premature optimization is root of all evil
สำหรับผม ผมมองว่าเราไม่ควรจะต้องคิดเยอะมากกับการทำเทส เราไม่ได้ทำเทสเพื่อให้เกิด Perfect software และเอาจริงๆ Perfect software ก็ไม่มีจริงในโลก แต่เราทำเทสให้ “ไม่ย้อนกลับไปเจอ Bug เดิม”
สำหรับผมเอง ในตอนเริ่มต้น TDD ผมจะพยายามร่างสเปคฟังก์ชั่นทั้งหมดออกมาให้ได้ แล้วนั่นน่ะเสียเวลามากๆ จนเป็นที่มาของความรู้สึกว่า TDD เสียเวลา
แต่ตอนนี้ผมทำ TDD แบบรู้สึกว่าบางครั้งทำได้เร็วกว่าเขียนไม่มีเทสต์อีก
Test one thing and only one thing
Make it work, make it right, then make it fast.
ผมพึ่งเข้าใจว่าทำไมคนที่พูดเรื่อง TDD ที่น่าจะช้าและละเอียดละออ กลับพูดอะไรที่ขัดกันก็ตอนนี้แหละ เพราะเค้าไม่ได้ตั้งใจให้เราทำทุกอย่าง Perfect แต่แรก และคนที่เริ่ม TDD ก็มักจะเข้าใจผิดและพยายามทำทุกอย่าง Perfect แต่แรกนี่เอง
สรุปสำหรับคนเริ่มต้น TDD ที่รู้สึกว่าตัวเองลำบากใจที่จะทำ นี่เป็นข้อแนะนำส่วนตัวของผม
- เขียนเทสให้โง่และเร็วที่สุด ควรจะโง่และใช้เวลาทำน้อยๆ ถึงแม้จะมีจำนวนบรรทัดเยอะ แต่มันจะโง่และเขียนได้เร็วเสมอ
- เทสเคสจะถูกเพิ่มเมื่อเจอ Bug
- อย่าคาดหวังว่าทำ TDD แล้วจะไม่มี Bug อย่าพยายามเขียนเทสให้ครอบคลุมทุกอย่างตั้งแต่แรก นั่นจะยิ่งทำให้ช้าและทำลาย Purpose ของการทำเทส เทสเราทำให้ไม่ต้องเสียเวลาคิดนานคิดแล้วคิดอีกก่อนแก้โปรแกรม แต่นี่เรากลับมานั่งคิดแล้วคิดอีกตอนเขียนเทส มันใช่เหรอ?
- สำหรับ Management หรือ Senior อยากให้ จำคำ Kent Beck ไว้ว่า TDD มีไว้ให้โปรแกรมเมอร์เลิกกลัวที่จะเขียนโค้ด เลิกกลัวที่จะแก้ไขโค้ด ถ้า TDD ทำให้ลูกทีมกลัวที่จะเขียนโค้ด แสดงว่ามีบางอย่างผิดพลาด การ “ลดความกลัวที่จะเขียนโค้ด” ควรจะเป็น Value สูงสุดในการทำ TDD เสมอ อย่าลืม Purpose นี้ อย่าเห็นแก่ Process จนลืมเป้าหมายเด็ดขาด
ผมคงมีเรื่องแชร์จากประสบการณ์แค่นี้ครับ