เริ่มต้นกับ Test Driven Development

ใคร เคยหลงทางในสนามบินบ้าง? คำตอบคือน่าจะยากมากเพราะอะไรเพราะในสนามบินเราจะพบกับการใช้ป้ายสัญลักษณ์ มากมาย ทุกครั้งที่เราเริ่มงงว่าจะเดินไปทางไหนเราจะเริ่มมองหาป้ายที่บอกทางไป Gate ของเราก่อนเลยเช่น “Gate 42: continue straight forward.” ตัวอย่างนี้สามารถเอามาประยุกต์ใช้กับการพัฒนาซอฟท์แวร์ในชีวิตประจำวันได้ เช่นกันลอง หลับตาคิดดูว่าถ้าเราอยู่ที่สนามบิน Heatthrow เราก้าวผ่านด่านตรวจคนแล้วพบป้ายใหญ่ๆที่เขียนว่า “โชคดี” แล้วก็ไม่เห็น ป้ายอะไรอีกเลยเราจะเดินไปทางไหน? หลงแน่นอน !!! ดังนั้นป้ายบอกทางเป็นสิ่งจำเป็นในสนามบินเช่นเดียวกันในโลกของการทำซอฟท์ แวร์เราพบว่าการทำ Test Driven Development ที่มีข้อ ปฏิบัติที่เรียบง่ายสามข้อนั้นเป็นเหมือนกับป้ายบอกทางหลายร้อยป้ายที่มี อยู่ที่สนามบินที่ช่วยนำเรา ไปในทิศทางที่ถูกต้องและสามารถไปถึงจุดหมาย ได้ตรงเวลาและอย่างที่เราได้อ่านกันมาหลายรอบแล้วว่าการทำ TDD คือการกลับการบวนการทำงานจาก design-code-test ที่ไม่สนุกเสียใหม่ให้เป็น test-code-refactor อันแสนสนุกแทนและ ใครจะเชื่อว่าไอ้กระบวนการอันแสนธรรมดาสามข้อนี้จะช่วยให้เราสร้างซอฟท์แวร์ ที่มีคุณภาพสูงออกมาได้อย่างที่เรา ไม่เคยทำได้มาก่อน นอกจากคุณภาพจะดีแล้วเรายังได้ระบบทีีถูกปรับเข้าหา requirement ตลอดเวลาทำให้ไม่หลงหรือพลาดไปจากสิ่งที่ลูกค้าต้องการ ค่าใช้จ่ายในการซ่อม defect ก็ต่ำกว่าเพราะมีกระบวนการตรวจสอบความถูกต้องที่ดีกว่า (automate test suite) นอกจากนี้มันยังช่วยให้เรามี productivity ที่สูงขึ้นอีกด้วยและเหนือสิ่งอื่นในการทำงานจะเต็มไปด้วยความมันส์
ใน บทนี้เราจะได้เรียนรู้ต่อไปอีกว่าอะไรคือ TDD และอะไรคือกุญแจแห่งความสำเร็จของ TDD เราจะได้ลงมือเขียน test-first กันนิดหน่อยเพื่อให้เห้นภาพการทำงานแบบ TDD

Figure 2.1 The TDD cycle is a three-step process of writing a test, making it pass by writing just enough production code, and finally improving the design by disciplined refactorings.
เนื่อง จากขั้นแรกของการทำ TDD คือทำ test fail ก่อนนั่นคือการสร้างกระบวนการทดสอบพฤติกรรมของระบบที่เราอยากได้ ดังนั้นอย่ารอช้ามาลงมือทำกันแต่เหนือสิ่งอื่นใดเราน่าจะมารู้เรื่อง requirement กันก่อน

2.1 จาก requirements สู่ tests
จินตนาการ ดูครับว่าเรากำลังทำระบบย่อยของระบบที่เรียกว่า corporate collaboration โดยส่วนที่เราทำนั้นรับผิดชอบใน การทำอีเมล์เทมเพลตสำหรับช่วยเลขาผู้บริหารให้สามารถส่งข้อมูลที่สำคัญๆไป ให้ใครก็ได้แบบง่ายๆด้วยการคลิ้กสองครั้ง เออแล้วเราจะ test ยังไง? เราจะได้คำตอบหลังจากอ่านเนื้อหาของบทนี้จบแล้ว โดยสิ่งแรกที่เราต้องทำคือการแตก requirement ออกเป็นส่วนย่อยๆที่เล็กลงและจับต้องได้

2.1.1 ระเบิด requirements
เมื่อ เรามี requirement สิ่งแรกที่เรามักทำกันคือแตกมันออกเป็นส่วนเล็กๆเป็น “งานที่เราต้องทำ เราเรียกมันว่า task” เมื่อเราทำมันเสร็จแล้วมันจะเป็นส่วนที่เติมเต็ม requirement ของเรา เออเราคิดแบบนี้ไม่ได้นะเราต้องคิดอีกแบบดังนั้นลืมไอ้ของเก่าที่คิดไว้ก่อน แล้วคิดใหม่คราวนี้เราย่อยมันออกเป็นชุดของ test เล็กๆ เมื่อเราให้ผ่านได้เราจะเติมเต็ม requirement ของเราได้ “นึกออกไหมครับ” น่าจะพอได้นะ มันอาจจะยากใน ครั้งแรกแต่เดี๋ยวจะค่อยๆชินไปเอง
“A mail template without any variable data is sent off as is.”
“The placeholder for the recipient’s first name in a greeting should get replaced with each individual recipient’s name, respectively.”
ค่อยๆทำไปเราจะได้ test ที่สามารถทดสอบพฤติกรรมของแอพพลิเคชั่นของเราได้เพื่อให้เราทำงานได้ง่ายขึ้นเราลองมาดูความแตกต่างระหว่าง Task กับ Test ก่อน จากตาราง 2.1 เราจะเห็นการเปรียบ เทียบระหว่าง task กับ test เรื่องหลักๆเลยคือการแตกงานออกมาเป็น Task แล้วเอามาเรียงกันเราจะไม่สามารถตรวจวัด ความคืบหน้าของงานได้เลย ในทางกลับกันถ้าเราแตกออกเป็น test แทนเราจะเห็นความต่อเนื่องและความก้าว หน้าของงานที่ทำได้อย่างง่ายดาย

Table 2.1 Alternative decompositions of a mail-template subsystem

เรา จะเห็นว่าการแตกงานออกมาเป็น test สุดยอดกว่าการแตกออกเป็น task เพราะอะไรเพราะ task นั้นคิดง่ายแต่ไม่ได้มี ความเชื่อมโยงอะไรกับงานเลยและก็ไม่ได้เป็นตัวนำทางที่ดีด้วยแถมดีไม่ดีทำ แล้วหลงอีกต่างหาก Task นั้นบอกแค่ว่าเราควรทำอะไร โดยทีี่ไม่บอกว่าเมื่อทำเสร็วแล้วเป็นยังไง แต่ไม่ต้องกลัวเดี๋ยวจะมีเนื้อหาที่ครอบคลุมและ อธิบายได้เรื่องนี้อีกในภายหลังแต่ก่อนอื่นเรามารู้สองเรื่องที่สำคัญๆก่อน

2.1.2 What are good tests made of?
มา ถึงตอนนี้เราได้รู้กันแล้วว่า test ดีกว่า task :) แต่ว่าไอ้ test ที่เราจะเขียนเราก็ต้องคิดให้ดีเหมือนกันว่าการเขียน test ที่ดีคืออะไร ถ้าเราลองไปศึกษาดูเราจะพบว่ามีการให้คำนิยามเกี่ยวกับการเขียน test ไว้มากมายและก็มีเทคนิคมากมาย ที่จะใช้สร้าง test อย่างไรก็ตามถ้าเรากลั่นสิ่งเหล่านั้นออกมาเราจะพบว่า test ใดๆก็ตามจะถือว่าเป็น test ที่ดีก็ต่อเมื่อมัน มีคุณสมบัติสองข้อต่อไปนี้

■ A good test is atomic.

■ A good test is isolated.
แล้ว คุณสมบัติสองข้อนี้สื่อถึงอะไร คุณสมบัติสองข้อนี้ต้องการบอกเราว่า test ที่ดีต้อง เล็ก มีจุดประสงค์ที่แน่นอน โฟกัสไปที่พฤติกรรมที่ต้องการ test และต้องเป็นอิสระต่อ test อื่นๆ เช่นต้องไม่มีการรอหรือใช้ผลลัพธ์จาก test อื่นๆก่อนหน้ามัน
ข้อ ดีของการการทำให้ test มีขนาดเล็กคือเราสามารถเดินตาม test ได้อย่างมั่นใจเพราะทุกๆ test ที่เกิดขึ้นคือก้าวเล็กๆที่ เราเดินไปข้างหน้าและเนื่องด้วยความสำคัญนี้เราจะต้องให้ความสำคัญกับการ เลือกลำดับของการเลือกโจทย์หรือปัญหาที่ ถูกต้องด้วยเช่นกัน

2.1.3 Working from a test list
ดัง นั้นจุดเริ่มต้นของงานเราคือเราต้องมีชุดของ test ที่เราต้องการทำก่อนจากนั้นเราจะมาเลือกกันว่า test ไหนจะเป็นผู้ โชคดีที่จะถูกทำก่อนโดยมากแล้วตัวที่เราจะเลือกก่อนคือตัวที่เราคิดว่ามัน ง่ายที่สุดเพื่อให้เราเดินหน้าได้เร็วที่สุดโดยใช้ แรงน้อยที่สุด และเมื่อเราเลือก test แรกที่จะถูกทำแล้วสิ่งต่อไปที่เราต้องทำคือ “ลืม” ไปก่อนว่า test อื่นๆยังมีอยู่เราจะ โฟกัสอยู่ที่ test ตัวนี้ที่จะถูกทำหลังจากนั้นเราจะเริ่มเขียน test code แล้วก็ compile และ execute มันเพื่อดูมัน fail :) ก่อนที่เราจะเริ่มเขียน production code หลายคนก็ยังคงงงอยู่ว่าเป็นไปได้หรอที่เราจะเขียน test สำหรับ code ที่ยังไม่เคยมีอยู่? เรามาดูกันว่ามันทำกันยังไงและทำไม

2.1.4 Programming by intention
มัน ทำได้ไง? เราจะจินตนาการอะไร? ถ้าเราจินตนาการถึง production code จากมุมมองของ test มันจะเป็นการโกหก ตัวเองหรือป่าว? ใช่มันคือการโกหกตัวเองแต่มันเป้นสิ่งที่ดีเพราะการที่เราเริ่มเขียน test ก่อนจะทำให้เราจินตนาการถึง production code ที่ถูกใช้งานได้ง่ายมาก เพราะไม่งั้นจะ test ยาก :) การคิดในลักษณะนี้ทำให้เรามั่นใจได้ว่า production code ของเราจะถูกสร้างออกมาให้ใช้งานได้ง่ายเพราะโดยสามัญสำนึกแล้วเราจะ จินตนาการถึงสิ่งที่ค่อนข้างธรรมดา และเข้าใจง่ายเสมอ นี่คือพลังของจินตนาการ และการทำงานแบบนี้มีชื่อเหมือนกันนะครับ “Programming by Intention”
ผล ของการทำงานตามแนวคิดของ Programming By Intension คือเราสามารถโฟกัสตัวเองไว้ที่จุดที่เราต้องการ test ขณะนั้นแทนที่เราจะด้น code มั่วไปเรื่อย นอกจากนี้ Programming By Intension ยังส่งผลให้โค้ด flow ขึ้นทำให้โค้ด อ่านง่าย ดังนั้นอย่ารอช้าตอนหน้ามาลองทำกันเลย

2 comments

  1. KwangTung

    ชอบคำว่า “ระเบิด requirements” ครับ…

    วันนี้ฟังบรรยายที่ CPE เสียดายเวลาน้อยไปนิด อยากอ่านบทความ หรือประสบการณ์ แนว ๆ การเก็บ requirements บ้างน่ะครับ บังเอิญหัวข้อวิจัยไปแนวนั้นพอดีครับ

Post a comment