สำหรับสายงาน Developer แล้ว นอกเหนือจากการที่เราต้องเขียนโปรแกรมเพื่อตอบโจทย์ Requirement ของลูกค้า สิ่งหนึ่งที่เราต้องคอยทำเพื่อไม่ให้เกิดความน่าปวดหัวในภายหลังก็คือ Testing นั่นเอง
เวลาที่มี Feature ใหม่ ๆ เข้ามา เราก็ต้องมาแก้ Code ที่เราเคยเขียนไว้เพื่อรองรับ Feature ใหม่ บางครั้งก็อาจจะง่ายถ้าหากเพื่อน ๆ รู้จักหลักการในการเขียน Clean code แต่อย่าลืมว่า Code ไม่ได้เป็นสิ่งเดียวที่เพื่อน ๆ จะต้องแก้ เพราะเราต้องแก้ Test cases ตาม Feature ใหม่ที่ได้รับมาอีกด้วย
แล้ว Test ของเพื่อน ๆ นั้นแก้ง่ายเหมือนเวลา Refactor code หรือเปล่า? วันนี้ The Gang Technology จะพาเพื่อน ๆ ไปดูเรื่องของการเขียน Clean test กัน!
ลองเปรียบเทียบระหว่าง
และ
ถ้าเป็นแบบแรก เราจะเข้าใจแค่ว่า Test cases ตัวนี้ทำการทดสอบระบบ Login แต่ว่าทดสอบยังไง? ทดสอบด้วย Parameter อะไร? เราไม่มีทางรู้เลยนอกจากจะเข้าไปอ่านว่า Test ข้างในทำอะไร ในทางกลับกัน ตัว Functions ทั้งสองอันด้านล่างสามารถอธิบายให้เราเข้าใจได้ชัดเจนโดยที่เราไม่ต้องอ่าน Code ข้างในด้วยซ้ำ
การตั้งชื่อตัวแปรให้มีความหมาย เราควรจะตั้งชื่อตัวแปรต่าง ๆ ให้มีความหมาย โดยเทคนิคก็จะมีดังนี้
- ตั้งชื่อตาม Naming convention ของภาษานั้น ๆ
- ตั้งชื่อตัวแปรให้กระชับด้วยหลัก Grammar ไม่ต้องใส่รายละเอียดทางเทคนิค เช่น customer_names vs customer_name_list
- ตั้งชื่อให้เกี่ยวข้องกับ Business logic magic number เช่น เลขคอลัมน์หรือ
String พิเศษ ก็ให้ตั้งเป็นตัวแปร เช่น
แทนที่จะใส่เป็นเลข Magic number แบบนี้ ให้ตั้งเป็น HEADER_ROW เป็นต้น
- ตั้งชื่อที่อ่านออกเสียงได้ เวลาที่ต้องเอาไปคุยกับทีมจะได้ไม่ลำบาก
- ตั้งชื่อให้เฉพาะเจาะจง ไม่กำกวม
- ตั้งชื่อให้แสดงถึงหน้าที่หรือการใช้งานของมัน แทนที่จะไปเขียนคอมเมนต์เพิ่มเติม การเขียนคอมเมนต์ก็ควรเป็นการอธิบายสิ่งต่าง ๆ นอกเหนือจากสิ่งที่ชื่อฟังก์ชัน หรือตัวแปรสามารถบอกได้
การเขียน Test โดยการไล่ลำดับ Arrange-Act-Assert (AAA)
นอกจากการตั้งชื่อให้ดีแล้ว ลำดับการเขียน Code ของเราเพื่อใช้ในการ Test ก็สำคัญด้วย โดยรูปแบบการเขียนควรจะเป็นตามนี้
- Arrange : หมายถึงการเตรียมข้อมูล ทำการ Mock ข้อมูลที่เราจะต้องใช้ในการ Test ให้เรียบร้อยเป็นลำดับแรกก่อนเสมอ
- Act : หมายถึงการกระทำ หรือการเรียกใช้ Function หรือ API ที่เราต้องการจะทดสอบ
- Assert : หมายถึงการยืนยันผลลัพธ์ที่ได้จากการ Act ในข้อ 2 ว่าถูกต้องตามที่เราตั้งใจไว้หรือไม่
หลักการเขียนด้วย F.I.R.S.T
- Fast : เพื่อน ๆ อย่าลืมว่าเมื่อเรา Develop ไปสักพัก ตัวระบบของเราก็จะใหญ่ขึ้นเรื่อย ๆ การที่เราเขียน Test ที่ใช้เวลานานจนเกินไปจะไปส่งผลต่อการหา Bug ในภายหลัง
- Independent : 1 Test case ไม่ควรจะผูกมัดกับ Test case อื่น ๆ
- Repeatable : Test case ควรจะสามารถถูกรันซ้ำ ๆ ได้ ไม่ว่าจะใน Environment ไหนก็ตาม
- Self - validating : Test case ควรจะสามารถบอกได้ด้วยตัวมันเองว่าTest นี้รันผ่านหรือไม่ผ่าน
- Thorough : Test case ที่ดีควรจะ Cover happy paths, Edge cases, Negative cases และในบางครั้งก็รวมไปถึงด้าน Security ด้วย
Test case ควรจะทดสอบพฤติกรรมหรือ flow เดียว
ใน 1 Test case เราอาจจะมีมากกว่า 1 Assert ก็ได้ แต่ทั้งนี้ทั้งนั้นควรจะทำการทดสอบพฤติกรรมหรือ Flow การใช้งานเพียงแค่ประเภทเดียวเท่านั้น ถ้าหากว่า Test case นั้นทำมากกว่า 1 อย่างแล้วละก็ เราควรจะ Refactor มันแยกออกไปเป็นอีก Test case แทน
ใช้ Mock data ที่มีความหมายและใกล้เคียงกับของจริงมากที่สุด
Test ถ้าเราเขียนให้ดี ก็ไม่ต่างอะไรกับตัวอย่างการใช้งาน Code เลย ดังนั้นแล้ว Data ที่เราจะนำไปใช้ทดสอบกับ Function ต่าง ๆ ของเรา ก็ควรจะ Mock ให้มันคล้ายคลึงกับของจริงให้มากที่สุดที่จะเป็นไปได้
ดังนั้นเราไม่ควรจะใช้ a,b,c,d หรือ 1,2,3,4 ถ้าเรารู้ว่า Data ที่ Test ของเราต้องเจอนั้นเป็นอย่างไร การที่เราทำแบบนี้ก็จะเป็นการช่วยหา Bug ให้ Function ของเราก่อนที่จะไปถึงมือ User ได้ดีอีกด้วย
เป็นอย่างไรกันบ้าง จากทั้งหมด 6 ข้อที่กล่าวมา มีข้อไหนที่เพื่อน ๆ ทำอยู่แล้ว หรือมีข้อไหนที่เป็นเรื่องใหม่ ๆ บ้างเอ่ย
สุดท้ายนี้ ข้อฝากประโยคนึงไว้ให้เพื่อน ๆ ชาว Developer
“Test ที่ดีจะอ่านเหมือนเรื่องราว พอเราได้อ่านจนจบก็จะเข้าใจว่าระบบนี้ทำอะไร โดยที่ไม่ต้องไปดู Code เลยแม้แต่บรรทัดเดียว”
หวังว่าทุกคนจะได้ประโยชน์จากบทความนี้ และสามารถเขียนให้เรื่องราวของตัวเองนั้นอ่านเข้าใจง่าย ด้วยหลัก Clean test กันนะ!