Screen Shot 2557-10-05 at 1.07.15 PM
ผมเชื่อว่า หลายๆ คนที่เขียน unit test นั้น
มักจะมีการจัดการ unit test ในรูปแบบดังนี้

ถ้าทำการทดสอบคลาส User จะสร้าง unit test ชื่อว่า UserTest ขึ้นมา
และใน UserTest จะประกอบไปด้วย test case หรือ test method จำนวนมากมาย

ซึ่งพบว่า เราก็จะทำการเพิ่ม test case เข้าไปเรื่อยๆ
ตามจำนวนของ requirement หรือ feature หรือตามเงื่อนไขต่างๆ  ที่เพิ่มเข้ามา
สุดท้าย พบว่า มันใหญ่มากๆ

แล้วเราแก้ไขอย่างไรกันล่ะ ?

วิธีการแก้ไขแรกก็คือ จัดกลุ่มด้วยชื่อ test case สิ

ยังทำการเพิ่ม test case เข้าไปยังคลาส Test เดิม
แต่มีการกำหนดรูปแบบของชื่อ test case ให้เป็นกลุ่มอย่างชัดเจน ดังนี้

  • add_emptyList
  • add_nonEmptyList
  • add_newUser
  • add_duplicateUser
  • search_foundUser
  • search_userNotFound
  • search_duplicateUser
  • size_ofEmptyList
  • size_ofNonEmptyList
  • size_foundUser
  • delete_onlyOneUser
  • delete_twoUser
  • delete_threeUser
  • delete_withEmptyUser

เป็นวิธีการที่ง่ายและเป็นแนวปฏิบัติพื้นฐาน และ คนส่วนใหญ่มักจะเลือกทำกัน

แต่ผมไม่แนะนำให้ทำ เพราะว่า

  • จำนวน test case มันเยอะมากๆ
  • เกิด Duplication code ขึ้นมาเยอะมากๆ ซึ่งมันคือ Code Smell ที่เกิดขึ้นมา

ดังนั้น ถ้าเป็นนักพัฒนาที่ดี เมื่อเห็นว่ามี Duplication code เกิดขึ้นมา
เราควรที่จะขจัดมันออกไปซะ
เพื่อทำให้ test ของเรามีโครงสร้างที่สวยงาม
นี่มันคือการ Refactoring test อย่างนึงเลยนะ !!

Screen Shot 2557-10-05 at 12.23.08 PM

จากรูปคือ วิธีการออกแบบระบบที่เรียบง่ายโดย JBrains
อธิบายไว้ว่า ระบบที่เรียบง่ายเกิดมาจาก 2 จุด คือ

  1. การปรับปรุงชื่อให้ดีขึ้น
  2. การขจัด duplication ออกไปจากระบบ

ซึ่งเราก็นำแนวคิดนี้มาใช้กับการจัดการ test เช่นเดียวกัน
ในเรื่องของการตั้งชื่อ เราก็สามารถตั้งกันได้ดีอยู่แล้ว !!!
ส่วนในเรื่องการขจัด duplication code ออกไปนั้น
ผมว่าหลายๆ คนคงเคยทำแบบนี้มาก่อน
คือ ตัวแปรที่ถูกใช้ในทุกๆ test case จึงทำการแแยกออกมา ดังตัวอย่าง

วิธีการดังกล่าวก็สามารถลด duplication code ลงไปได้เล็กน้อย
แต่มันก็ดีกว่าไม่ทำอะไรเลย ใช่ไหม!!!

แต่ว่าเราจะพบว่า จาก test case ต่างๆ ใน UserTest นั้นมีส่วนที่ซ้ำกันมากมาย
ดังนั้น วิธีการที่ขอแนะนำในการจัดการก็คือ การจัดกลุ่มของ test

แล้วทำการจัดกลุ่มของ Test อย่างไรล่ะ

ในการจัดกลุ่มนั้นควรจัดกลุ่มตามพฤติกรรมการทำงาน ไม่ใช่ method นะ
ตัวอย่างเช่น ในแต่ละ method อาจจะใช้ข้อมูลเริ่มต้นที่เหมือนกันเสมอ
ดังนั้น ถ้าเราพัฒนา test case ด้วย jUnit สามารถแยกส่วนของการกำหนดข้อมูลไว้ใน @Before ได้

ต่อมา เราจะพบว่า แต่ละ test case มันอาจจะมีพฤติกรรมการทำงานที่แตกต่างกันไป
จะสังเกตได้จาก ไม่สามารถใช้ค่าเริ่มต้นต่างๆ เหมือนกันได้
ดังนั้น เราควรที่จะแยก test case เหล่านั้นออกไปจาก test นี้ซะ

ตัวอย่างจากของ UserTest สามารถแบ่งกลุ่มออกไปได้ดังนี้ (แนวคิดของการจัดกลุ่มอาจจะแตกต่างกันไปของแต่ละคน)

  • EmptyUserTest
    • add_emptyList
    • size_ofEmptyList
  • NonEmptyUserTest
    • add_nonEmptyList
    • add_newUser
    • add_duplicateUser
    • size_ofNonEmptyList
  • SearchUserTest
    • search_foundUser
    • search_userNotFound
    • size_foundUser
    • search_duplicateUser
  • UserDeleteTest
    • delete_onlyOneUser
    • delete_twoUser
    • delete_threeUser
    • delete_withEmptyUser

โดยแต่ละกลุ่มของ test นั้นมักจะมีค่าเริ่มต้นต่างๆ เหมือนกัน
ต่างกันเพียงผลลัพธ์ที่คาดหวังเท่านั้น
ดังนั้น มันจะไม่ทำให้คุณกลัว เมื่อต้องกลับมาแก้ไข
หรือต้องกลับมาดูเมื่อเกิดความผิดพลาดต่างๆ ขึ้นมา

ซึ่ง test ต่างๆ เหล่านี้ก็ให้เขียนเป็นไฟล์ใหม่ออกมา ดังนี้

  • EmptyUserTest
  • NonEmptyUserTest
  • SearchUserTest
  • UserDeleteTest

ซึ่งขอสรุปขั้นตอนการจัดกลุ่มของ test ง่ายๆ ไว้ดังนี้

จัดกลุ่มโดยใช้ชื่อนำหน้าของแต่ละ test case ก่อน
ต่อมาจึงทำการจัดกลุ่มด้วยข้อมูลที่ใช้ในการทดสอบ
หลังจากนั้นให้แยกกลุ่มตาม action หรือ การกระทำของ test case นั้น
ลองทำดูครับ แล้วจะรู้ว่า test case ที่มีการจัดการโครงสร้างดีๆ มันมีประโยชน์อะไรบ้าง

ดังนั้น เมื่อไรก็ตามที่เราเจอ Code Smell หรือ code ที่มันแปลกๆ เช่น ชื่อไม่สื่อ และมี duplication code
ก็ขอให้รีบทำการแก้ไข ไม่มากก็น้อย แต่ขอให้มันดีขึ้นกว่าเดิมครับ

วันนี้ คุณ ทำ การ Refactoring Test แล้วหรือยัง !!!