Friday, October 2, 2015

Recap สรุปจาก Practical unit testing (GDC 2014)

ครับ จาก Anti-pattern ทั้ง 4 ตัวที่เขียนไป บล็อกนี้เราจะมาทบทวนดูกันอีกทีว่าการเขียน Unit test ที่ดีควรปฏิบัติ

Respect unit test source code as much as production source code

ก็คือให้เคารพโค้ดใน  unit test เช่นเดียวกันกับโค้ดใน production นะครับ

และอย่าลืมยึดหลัก Good unit test นั่นคือ Readable, Maintainable และ Trustworthy


Write once, read many
พึงระลึกไว้อยู่เสมอว่าโค้ดมันถูกอ่านมากกว่าเขียน


Only one explicit assumption.
 หรือให้มี assertion แค่อันเดียวหรือเป้าหมายเดียวครับ

Minimize implicit assumptions
พยายามลด Implicit assumptions หรือ integration test ใน unit test ลงให้เหลือน้อยที่สุด


หมดแล้วจ้า ยังไงลองเข้าไปดูวีดีโออีกทีนะครับ มีหลายอย่างที่ผมไม่ได้หยิบมาเขียน อาจจะได้อะไรเพิ่มเติมครับ
นอกจากนี้ในส่วนของ description ใน youtube ยังมี link ไปยังหนังสือต่างๆที่แนะนำให้อ่านกันด้วยนะครับ เกี่ยวกับการเขียน test มีประโยชน์มากๆ



สวัสดีครับ

สรุปจาก Practical unit testing (GDC 2014): Wide anti-pattern

มาถึง anti-pattern สุดท้ายกันแล้วครับก็คือ Wide anti-pattern

ก็คือเมื่อเวลาที่เราไปแก้ไขโค้ดแก้บั๊กสักตัวนึงแล้วลองรัน Unit test ดูแล้วก็พบว่า Error บานอีกเช่นเคย

ที่นี้มาดูโค้ดกันหน่อย ดูดีจะเห็นว่า test นี้กำลังทดสองโมดูลที่เชื่อ DraftBehavior แต่ Assertion ดันไปอยู่ที่คลาส BehaviorSystem ซึ่งตัวคลาส BehaviorSystem นั้นก็มี  unit test ของมันอยู่แล้ว ดังนั้นการที่เรามาเรียกใช้คลาสอื่นๆใน unit test นั้นจะถือว่าเป็น implicit assumption ก็คือ ใน test นี้ เราไม่ได้ทดสอบแค่คลาส DraftBehavior เท่านั้น แต่ BehaviorSystem นั้นถูกทดสอบไปด้วย
ดังนั้น test นี้จึงไม่ใช้ Unit test อีกต่อไปละครับ ต้องเรียกว่า Integration test แทน

ซึ่งเราอาจจะสรุปได้ว่า
"Test ที่มี Implicit assumptions มากกว่า 1 อัน ไม่นับว่าเป็น Unit test"


ไม่ได้บอกว่า Integration test ไม่ดีนะครับ แต่ต้องทำให้ถูกที่ ถูกทาง ถูกจังหวะของมัน ไม่อย่างนั้นมันก็จะมาขวางทางเราแบบนี้แหละครับ ซึ่งเกี่ยวกับ Integration test นั้น เดี่ยวจะมาเล่าอีกทีครับ

มาดูกันต่อว่าจะแก้ปัญหานี้อย่างไรดี

ตอนที่เรื่มเขียน test นั้น มันอาจจะมี logic บางอย่างที่ในคลาส BehaviorSystem ซึ่งต้องทำการ setup ให้ถูกต้องเสียก่อนถึงจะให้ผลลัพท์ที่ถูกต้องได้

รู้สึกเหมือนกำลัง implement logic นี้สองที่เลยนะครับ ทั้งในโค้ดจริง ทั้ง test เลย

ทางแก้ที่แนะนำก้คือให้หาสิ่งที่เป็น "รอยต่อ" ระหว่างสองคลาสนี้ แล้วนำเทคนิค Test double และ Inversion of control มาใช้ครับ ในตัวอย่างนี้ก็คือ จริงๆแล้ว DraftBehavior.updateImpl() นั้น ต้องการ Heatmap จาก BehaviorSystem


พอเจอจุดที่เป็นรอยต่อแล้วเราก็ทำการสร้าง MockHeatmap ขึ้นมาครับ กำหนดให้มันเหมือนกับตัวที่คลาส BehaviorSystem จะส่งให้เวลาเรียก DraftBehavior.updatImpl()

ที่นี้ ก็ไม่จำเป็นต้องมีการเรียกใช้ BehaviorSystem ใน unit test ของ DraftBehavior แล้วครับ

สำหรับวิธีการในการหารอยต่อ สำหรับมือใหม่อาจจะหายากสักหน่อย แนะนำให้ลองอ่านหนังสือ Working effectively with legacy code ดูครับ

สรุปอีกที่ Wide anti-pattern ก็คือการที่ unit test มันมีการไปเรียกคนอื่นที่ไม่ใช่สิ่งที่เราสนใจใน unit test อันนี้ ซึ่งทำให้เราตกอยู่ในความเสี่ยง มีโอกาสสูงมากที่เราแก้บั๊กหรือเพิ่มฟีเจอร์ใหม่ๆเข้าไปแล้วจำทำให้ unit test ของเราพังครับ

เดี๋ยวบล็อกหน้ามา Recap ทั้งหมดกันอีกทีนะครับ