Screen Shot 2558-09-23 at 5.02.58 PM
ในการเขียนการทดสอบแบบอัตโนมัตินั้น (Automated testing)
มีรูปแบบ แนวคิด และ เทคนิคการเขียนที่ดีมากมาย
แต่ developer มักจะบอกว่า มันเยอะมาก ๆ
ทำไปก็เหนื่อยเพิ่มขึ้นอีก เสียเวลา … สุดท้ายก็ไม่ทำ !!

ดังนั้นเพื่อให้มันเข้าใจง่ายกว่าเดิม
มาพูดถึงวิธีการที่มันแย่ ๆ ดีกว่า
เพื่อทำให้รู้ ให้เห็น แลเข้าใจได้ง่ายขึ้น

ข้อดีของการมี Automated test

ถ้าใครเคยฟัง และ เขียนพวก Automated test มา
จะรู้ว่ามันมีข้อดีมากมาย เช่น

  • ทำให้การ refactor code ง่าย และ ปลอดภัย
  • ช่วยทำให้การ regression ง่าย เร็ว ถูก
  • ทำให้เรามี specification และ เอกสารที่สามารถประมวลผลได้
  • ช่วยลดเวลาในการสร้าง software
  • ช่วยลดค่าใช้จ่ายในการสร้าง software

พอพูดถึง test ที่ดี ก็มักจะพูดถึง F.I.R.S.T

ถึงเวลาของการเขียนแบบแย่ ๆ หรือ Bad Practice กันล่ะ

โดยจะเน้นที่ unit test นะ มาเริ่มกันเลย
แต่ละข้อเป็นสิ่งที่เข้าใจผิดกันเยอะ

1. อย่าทำการทดสอบ Private method

private มันคือ private
สร้างขึ้นมาเพื่อให้ใช้เฉพาะภายใน class เท่านั้น
การที่จะทดสอบ private method จึงเป็นเรื่องที่ไม่น่าทำ !!
ทำให้เราสร้าง private method ขึ้นมาจำนวนมาก !!

ซึ่งจริง ๆ แล้วมันคือ สิ่งที่เข้าใจผิดอย่างมาก
เนื่องจากถ้าคุณต้องการทดสอบ private method
ดังนั้นเปลี่ยนจาก private เป็น public ซะ
จะได้ทดสอบได้

ในการทำงานจริง ๆ private method มันถูกเรียกใช้งานจาก public method อยู่แล้ว
ดังนั้นก็ให้ทดสอบผ่าน public method สิ

แต่ถ้าบอกว่า private method มันถูกใช้งานหลายส่วนมาก
แต่ถ้าบอกว่า private method มันถูกแก้ไขบ่อยมาก
หรือ private method มันไม่น่าจะใช่ส่วนหนึ่งของ public method อีกต่อไปแล้ว
ดังนั้นจะทำอย่างไรดีล่ะ ?

ดังนั้นควรแยกการทำงานไปยัง class ใหม่ได้แล้วนะ
และเปลี่ยนให้ method เป็น public
นั่นคือ method นี้สามารถทดสอบได้แล้วนะ

ประเด็นที่สำคัญคือ
แล้วไอ้ private method ก่อนหน้านี้จำทำอย่างไรล่ะ ?
คำตอบคือ
ก็ให้เรียกใช้งาน class ใหม่ไงล่ะ หรือ delegate นั่นเอง
ซึ่งมันจะไม่ส่งผลกระทบต่อการทำงานเดิม
(ไม่เปลี่ยนพฤติกรรมการทำงานนะ นั่นคือ Refactoring)

2. ไม่ทำการ stub พวก External library

เป็นสิ่งที่น่ากลัวมาก ๆ สำหรับการใช้งานพวก 3-party library
อย่าทำการอ้างถึง หรือใช้งาน library เหล่านี้โดยตรง
ยิ่งใน test แล้วห้ามโดยเด็ดขาด
ดังนั้น การใช้งานต้อง stub มันซะ (เรื่อง Test Double มันสำคัญมาก)

หรือสร้าง class สำหรับห่อหุ่มการทำงานเรื่องนั้น ๆ ไว้ซะ
มันทำให้คุณสามารถเปลี่ยน 3-party library ได้ง่าย
เช่น

ในวันนี้คุณใช้งาน HTTP Library ที่ 1
และต่อมาคุณต้องการเปลี่ยนไปใช้ HTTP Library 2
คำถาม คือ คุณจะทำอย่างไรล่ะ ?
คำตอบคือ
สร้าง abstraction layer หรือ interface หนึ่ง
สำหรับการติดต่อผ่าน HTTP protocol มาสิ
จากนั้นก็สร้าง implementation class จาก interface เอา
ทำให้เวลาเราต้องการเปลี่ยน หรือ ทดสอบง่ายขึ้นนั่นเอง

อีกอย่างถ้าคุณทำการ stub พวก dependency เหล่านี้
ต้องพิจารณาด้วยว่า มันคุ้มค่า หรือ เหมาะสมหรือไม่
ไม่ใช่ตั้งหน้าตั้งตา stub ให้เยอะที่สุด
เดี๋ยวจะกลายเป็น Stub/Mock bubble ไปอีก
ซึ่งมันบ่งบอกว่า การออกแบบ code แย่มากมาย

3. อย่าทำการทดสอบ Constructor

หลาย ๆ คนบอกว่า
เราแยกส่วนการทำงานออกจาก constructor นะ
ใน constructor มันจึงไม่มีการทำงานใด ๆ หรอกนะ
ดังนั้น เราไม่จำเป็นต้องทดสอบหรอกนะ !!

ก่อนอื่นลองกลับไปดูใน constructor จาก code ของคุณก่อนนะ
ว่าใน constructor มีการทำงานเยอะไหม ?
ว่าใน constructor มี dependency เยอะไหม ?

ถ้าเยอะ มันหมายความว่าได้เวลาทดสอบแล้วใช่ไหม ?
ตอบได้เลยว่า ใช่แล้ว
แต่มันยังไม่พอนะ
ต้องทำการแยกส่วนการทำงานนั้น ๆ ออกไปจาก constructor ด้วย
เพราะว่ามันคือ code แปลก ๆ หรือ Code Smell
ซึ่ง developer ทุกคนควรศึกษาหาความรู้เพิ่มเติมด้วยนะ

บางคนถามว่า จะทดสอบ constructor ยังไงล่ะ ?
มันไม่ return อะไรกลับมาเลยนะ
ตอบได้ง่าย ๆ คือ
เราก็ทำการส่งสายลับ หรือ spy เข้าไป
และทำงานร่วมกับ Stub สิ
เพื่อตรวจสอบการเรียกใช้งาน constructor นั้น ๆ หรือไม่ ?
แต่มันดี สำหรับ constructor ที่ไม่ทำอะไรเลยเท่านั้นนะ

ยิ่งไปกว่านั้น ถ้าพบว่า class นั้น ๆ มันมี constructor จำนวนมาก ๆ
นั่นคือ Code Smell อีกแล้ว
ให้ทำการแยกออกมาเป็น class เล็ก ๆ
เพื่อทำงานแต่ละอย่างไปดีกว่านะครับ
หรือสร้าง creation method สำหรับการสร้างแต่ละแบบ
น่าจะเป็นอีกทางเลือกที่น่าสนใจ

4. ทดสอบ method ให้ครบ

การทดสอบคือ การทดสอบพฤติกรรมการทำงานของ code ในระบบ
ว่าเป็นไปตามที่เราต้องการหรือไม่
ดังนั้น อย่าไปยึดติดว่า ต้องทดสอบ method
ไม่เช่นนั้นมันจะกลายเป็นว่า unit test มันไป lock การทำงานของ code มากเกินไป
สุดท้าย เมื่อมีการเปลี่ยนแปลง requirement เพียงเล็กน้อย
จะกระทบกับ test จำนวนมาก
แทนที่จะช่วย กลับทำร้ายเรามากมายอีกนะ

ผมเคยอ่านหนังสือเกี่ยวกับการแพทย์

เขาบอกไว้ว่า กว่าที่แพทย์จะเชื่อว่า
การล้างมือก่อนการผ่าตัดนั้น
มันช่วยลดอัตราการเสียชีวิตของคนไข้
ต้องใช้เวลาเป็นสิบ ๆ ปี

การเขียน test ก็เช่นกัน คงต้องใช้เวลาในการพิสูจน์
แต่คุณลองเขียนแล้วหรือยังล่ะ ?

สุดท้ายแล้ว

การเขียน test มันคือการลงทุน
แต่จากประสบการณ์มันคุ้มมากมายสำหรับการลงทุน

ดังนั้นเริ่มต้นควรเผื่อเวลาสำหรับมันไว้ด้วย
เนื่องจากมันใช้สำหรับการ maintain ระบบ (ปกติก็ต้องดูแลระบบใช่ไหม ?)

ลองถามตัวเองสิว่า
ก่อนที่จะสร้างอะไรขึ้นมาสักอย่าง
คุณไม่รู้จริง ๆ หรือว่าจะทดสอบมันอย่างไร ?

ปล.
developer หลาย ๆ คนมักบอกว่า ไม่รู้จะทดสอบอะไร ?
นั่นหมายความว่า developer กำลังจะสร้างอะไรล่ะ ?
Developer กำลังสร้างของมั่ว ๆ ขึ้นมาหรืออย่างไร ?

 

Reference Website
https://www.objc.io/issues/15-testing/bad-testing-practices/