ในการเขียน Unit test นั้น มันจะมีประโยชน์ และ มีประสิทธิภาพ สำหรับทีมพัฒนา
แต่ unit test จำเป็นต้องมีคุณสมบัติ FIRST ดังนี้
- Fast
- Isolated
- Repeatable
- Self-verify
- Timely
มาดูกันว่าแต่ละหัวข้อนั้นคืออะไร และเป็นอย่างไร ?
แล้วลองมองย้อนกลับไปว่า Unit test ที่เขียนหรือสร้างกันขึ้นมานั้น
มันมีคุณสมบัติต่างๆ เหล่านี้ครบหรือไม่ ?
F = Fast
ในการให้ unit test ทำงานจะต้องทำงานได้รวดเร็ว
ยิ่งเร็วมากเท่าไร คุณจะสามารถสั่งให้ unit test ทำงานได้บ่อยมากขึ้นเท่านั้น
ยิ่งเร็วมากเท่าไร คุณจะได้รับ feedback กลับมาเร็วมากขึ้นเท่านั้น
ลองคิดดูว่า ถ้า unit test ของคุณทำงาน 1 นาที ? คุณจะสั่งให้ unit test ทำงานตอนไหน
ลองคิดดูว่า ถ้า unit test ของคุณทำงาน 5 นาที ? คุณจะสั่งให้ unit test ทำงานตอนไหน
ถ้าทำงานเป็นชั่วโมงล่ะ …
ดังนั้น สิ่งที่ดีที่สุดคือ ใช้เวลาในการทำงานน้อยที่สุด หรือ ทำงานเร็วที่สุดนั่นเอง
ถามว่าเร็วนี่มันเท่าไร ตอบได้ง่ายๆ คือ เร็วกว่าเดิม
ลองคิดดูว่า ในระบบที่เราสร้างขึ้นมามันน่าจะมี unit test เป็นร้อยเป็นพัน
ดังนั้น เรื่องความเร็วจึงสำคัญมากๆ
I = Isolated
ใน unit test แต่ละตัวนั้น มีเหตุผลที่จะทำงานผิดพลาดเพียงเหตุผลเดียวเท่านั้น
หรือพูดง่ายๆ คือ ในแต่ละ unit test ควรมีการตรวจสอบเพียงอย่างเดียว
หรือมีเป้าหมายเดียวเท่านั้น
ถ้าใน unit test ตัวไหนมีการทดสอบพฤติกรรมมากกว่า 1 เรื่อง
ให้ทำการแยกออกมาอีก unit test ซะ ให้มันเล็กลงมา
และทำการแก้ไขชื่อของ unit test เพื่อให้มันสามารถสื่อสารได้ชัดเจนขึ้น
โดยโครงสร้างของ Unit test ที่ดี ควรประกอบไปด้วย 3 ส่วน คือ
- A = Arrange
- A = Act
- A = Assert
ใน unit test แต่ละตัว ต้องมันขึ้นอยู่ต่อกัน
นั่นคือสามารถทำงานได้อย่างเป็นอิสระ ส่วนไหนจะทำงานก่อนหลังก็ได้
ส่งผลทำให้เราสามารถหาจุดที่ทำงานผิดพลาด และแก้ไขได้ง่าย
ไม่เช่นนั้นก็ต้องมานั่ง debug เช่นเดิม
R = Repeatable
Unit test ที่ดีต้องสามารถทำงานซ้ำๆ ได้
โดยไม่ขึ้นอยู่กับส่วนการทำงานอื่นๆ ทั้งภายในและภายนอก เช่น
- ระบบไฟล์
- Database
- Thread/Process
- External service
- Network
ซึ่ง unit test ต้องสามารถทำงานได้ ถ้าระบบที่เกี่ยวข้องทำงานไม่ได้ หรือไม่มีอยู่จริง
เนื่องจาก unit test มันใช้ทดสอบภายในระบบ ไม่ใช่ภายนอกระบบนะ
ดังนั้น ถ้าเพิ่มให้ unit test มัน Isolate มากขึ้นเท่าไร
ก็จะยิ่งทำให้สามารถทำงานซ้ำๆ ได้ง่ายขึ้นเท่านั้น
S = Self-verify
Unit test ที่ดีจะต้องแสดงให้เห็นว่าผ่านหรือไม่ผ่านอย่างชัดเจน
เมื่อเราเห็นว่ามันผ่านทั้งหมด เราจะมั่นใจได้ว่า code ที่เราสร้างมานั่นทำงานได้อย่างถูกต้อง
พร้อมที่จะก้าวต่อไปแล้วนะ
แต่ถ้าเกิดไม่ผ่านขึ้นมา เราจะต้องทำการแก้ไขให้มันผ่านก่อนนะ
ปัญหาหลักๆ จะเกิดตอนที่การทดสอบมันไม่ผ่าน
แล้วเราจะต้องมาดูว่า unit test ไหนบ้างไม่ผ่าน
บ่อยครั้งเรามักจะพบ unit test ที่ไม่ชัดเจน
ดังนั้นสิ่งที่ developer มักจะทำก็คือ การ print ค่าต่างๆ ใน unit test ออกมา !!
เพื่อดูว่าค่าต่างๆ ออกมาตามที่คาดหวังหรือไม่
ซึ่งนั่น มันแสดงให้เห็นว่า unit test ไม่ดีนะ
สามารถแก้ไขด้วยการสร้าง unit test ใหม่ หรือ ใส่ Assert เพื่อตรวจสอบไปซะ
ไม่เช่นนั้น จะเกิด unit test แบบนี้เต็มระบบกันเลยทีเดียว
ซึ่งนั่นแหละ คือ ปัญหาที่น่ากลัวมากๆ
หรือถ้า unit test ใดที่ทีมดูแล้วว่า ไร้ประโยชน์ ก็ให้ทำการลบทิ้งไปซะ
แต่การลบออกไปอาจจะกระทบต่อ Metric ต่างๆ ที่ทีมตั้งไว้ เช่น test coverage
แต่นั่นไม่ใช่ประเด็นหลักสำหรับทีมนะ
T = Timely
คำถาม
เราควรต้องเขียน code ของการทดสอบ ก่อนเขียน production code หรือเปล่านะ ?
คำตอบ
ผมว่ามันไม่ใช่ประเด็นเท่าไร แต่
ถ้าคุณเขียน test ก่อนสร้าง production code ขึ้นมา
มันจะทำให้เห็นว่า คุณเข้าใจปัญหา คุณกำลังจะสร้างพฤติกรรมอะไรขึ้นมา
หรือมันคือการเขียน Specification by example ขึ้นมาก่อน
นั่นมันช่วยทำให้เราเข้าใจว่ากำลังทำ และ จะทำอะไร
ดังนั้น จึงเป็นที่มาว่าทำไมต้องเขียน test ก่อน
คำถาม
การเขียน test หลังจากที่พัฒนา production code แล้ว มันช่วยเรื่องคุณภาพหรือเปล่านะ ?
คำตอบ
อันนี้ไม่รู้เหมือนกัน แต่เชื่อว่า คุณจะไม่ได้รับประโยชน์เหมือนกับการเขียน test ก่อนแน่นอน
แต่ประเด็นหลักๆ คือ ควรที่จะเขียนให้ถูกที่ถูกเวลา
… แล้ว Unit Test ของเรามัน FIRST หรือเปล่านะ ….
Reference Website
https://pragprog.com/magazines/2012-01/unit-tests-are-first
http://agileinaflash.blogspot.com/2009/02/first.html
http://c2.com/cgi/wiki?CodeUnitTestFirst