มาถึง 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 ทั้งหมดกันอีกทีนะครับ