Unit test ตอนที่ 2 เริ่มต้นเขียน Unit test แบบง่ายๆ

เริ่มต้นเขียน Unit test แบบง่ายๆ

เริ่มเขียน Unit test ที่ง่ายที่สุดในโลก

ผมเขียนเกี่ยวกับ Unit test ไว้ 4 ตอน คุณสามารถกด Link ด้านล่างเพื่ออ่านที่เกี่ยวกับ Unit test ตอนต่างๆได้เลย

ตอนนี้มาเขียน Unit test แบบง่ายๆกันก่อนเลย ตามความหมายที่ผมให้ไปบทที่แล้ว (ความหมายไม่เป็นทางการ ไม่ต้องเอาไปตอบอาจารย์ตอนสอบนะ) ว่า Unit test คือ “การเขียน Code ไว้ Test Code ส่วนหนึ่งว่ามันทำงานถูกต้องตามที่ต้องการหรือไม่ โดยไม่ต้องติดต่อกับส่วนจริงๆที่เกี่ยวข้องกับ Code ส่วนนั้น” วันนี้เรามาเริ่มง่ายๆกับการ “การเขียน Code ไว้ Test Code ส่วนหนึ่งว่ามันทำงานถูกต้องตามที่ต้องการหรือไม่”

ในส่วนที่จะเขียน Unit test ต่อจากนี้ทั้งหมดอันนี้ผมขอเขียนด้วยภาษา Java ใช้ Junit5 เป็นตัวทำ Unit test แต่ขอบอกไว้ตรงนี้เลยว่า ถ้าคุณเข้าใจหลักการวิธีการเขียน คุณจะไปใช้กับภาษาอะไรก็ได้ เพราะหลักการ แนวทาง วิธีเขียนมันก็จะไปในทางนี้แหละ แค่ต้องไปหาว่า Lib หรือ Frame work นั้นเขาใช้คำสั่งหรือวิธีไหนเมื่อเทียบกับ Junit5

Set up กันก่อนนะครับ

ถ้าขี้เกียจเขียน Code หรือสร้าง Project ขึ้นมาเอง ก็สามารถใช้ source code ของผมที่ https://github.com/surapong-taotiamton/example-unit-test.git

โดยสามารถนำเข้าตัว eclipse โดยสามารถทำตาม Link นี้ได้เลย

เนื่องจากผมใช้ junit5 ซึ่งมันไม่ได้ใส่มาโดยอัตโนมัติพร้อมกับ Eclipse (เพราะมันยังใหม่ระดับหนึ่ง) ดังนั้นเลยต้องทำการ Config เพิ่มให้กับ Eclipse โดยสามารถทำตาม Link นี้ได้เลย

ตัวอย่างมาตรฐาน

ถ้าพูดถึงตัวอย่างมาตรฐานก็ Code การบวกเลขเลย อันนี้ไม่ต้องสนใจมากแค่เอาเลขมาบวกกัน

ต่อไปเป็นส่วน Test ครับ

มาเริ่มอธิบายกันเลย

@Test

ส่วนนี้เป็นการบอกให้ตัว Junit ทราบว่า method นี้จะเป็น unit test โดยใน 1 Class จะมีกี่ unit test ก็ได้ แต่ตามหลักการนะครับ 1 unit test ควรจะ Test แค่ 1 เรื่อง โดยจากตัวอย่างของผม Unit test อันนี้คือ

plus_Normal_ReturnSuccessValue

ตัว Unit test มีหลักการตั้งชื่อนะครับ เพื่อให้เข้าใจว่า Unit test นี้กำลังจะ Test อะไร Case ไหน และผลลัพธ์ที่ควรได้คืออะไร เพื่อให้เวลา Run Unit test ไม่ผ่านจะได้รู้คร่าวๆว่าติดที่ Case ไหน โดยหลักการตั้งชื่อที่นิยมกันสามารถอ่านได้ตามนี้เลย https://dzone.com/articles/7-popular-unit-test-naming

โดยจากตัวอย่างผมตั้งชื่อตามหลัก MethodName_StateUnderTest_ExpectedBehavior

Body ของ Unit test

ตัว Unit test ตามหลักการที่ผมตามอ่านมา จะแบ่งออกเป็น 3 ส่วนหลักๆคือ ARRANGE , ACT, ASSERT

ARRANGE

ในส่วนนี้จะเป็นการตั้งค่าต่างๆตัวอย่างเช่น กำหนดค่า Input ที่จะส่งเข้าไปในส่วนที่ Test , กำหนดผลลัพธ์ที่คาดว่าน่าจะได้ , กำหนด Environment ต่างๆเพื่อจะให้เกิด Case ที่ต้องการ คุณฟังไม่ผิดครับ คุณสามารถสร้าง Case อะไรก็ได้ ตราบเท่าที่คุณออกแบบให้มันเอื้ออำนวย

ACT

ในส่วนนี้คือส่วนที่สั่งให้ Code ส่วนที่ต้องการ Test ทำงาน ในส่วนนี้ไม่ค่อยมีอะไรมาก จริงๆอาจจะน้อยที่สุดเลยด้วย

ASSERT

ส่วนนี้จะเป็นการตรวจสอบว่า Code ส่วนที่ Test ทำงานถูกต้องหรือไม่ โดยการตรวจสอบจะมีการตรวจสอบค่า Return ออกมา หรือ ตรวจสอบการทำงานของส่วนอื่นของ Code (ส่วนนี้ยังไม่พูดถึง) โดยจากตัวอย่าง ผมทำการ Test ว่ามันเอาค่ามาบวกกันแล้วได้ค่าถูกต้องไหม โดยใช้ method ที่ชื่อว่า assertEquals โดย method ทำนองนี้มีมากมายหลายตัวเลย ไปทดลองใช้กันดูนะครับ

ข้อแนะนำ และ ควรทำตาม

  1. ใน 1 Unit Test นั้นควรจะทำการ Test แค่เรื่องเดียวกรณีเดียว อย่าไป Test หลายอย่างใน Unit Test เดียว เพราะมันจะทำให้ Unit test นั้นซับซ้อน แล้วถ้ามันซับซ้อนอ่านยาก เข้าใจยาก และอาจจะทำให้เกิด Bug ใน Unit test เอาง่ายๆ สุดท้าย พยายามทำให้ง่ายเข้าไว้

  2. พยายามเขียน Unit test ให้เป็นระเบียบ เขียนให้อ่านง่าย เหมือนกับเขียน Code ครับ อย่าลืมว่า Unit test ก็เป็น Code และมันมีโอกาสที่จะมีคนมาอ่านต่อ หรือ แก้ไข Unit test นี้ ดังนั้นเขียน Unit test นี้ให้ดี ให้เรียบร้อยครับ

ตัวอย่างที่ยากขึ้นมาหน่อย

อันนี้สมมุติว่ามีคนบอกว่าต้องการ Util Class ที่ทำงานเกี่ยวกับ Order โดย Class นี้มีหนึ่ง Method ที่ให้หา index ของ Array ตาม Order ทีส่งเข้าไป โดยกฏเบื้องต้นคือ Array ต้อง Unique งงล่ะสิ ผมก็ไม่รู้จะอธิบายยังไงอธิบายเป็น Case ละกัน

method : int indexOfOrder(int[] array, int order)

Array = { 3, 1, 2}

Order ที่ต้องการคือ 3

ความหมายของมันก็คือ หา index ของค่าที่อยู่ที่ลำดับ 3 ของ Array นี้ จากตัวอย่าง อันดับ 3 ของ Array คือ 3 ซึ่ง 3 อยู่ที่ index 0 (เริ่มนับ index ที่ 0) ดังนั้นคำคอบของกรณีนี้คือ 0

คิด Case แล้วเขียน Unit test

โอเค Case แบบธรรมดาทั่วไปมีเท่านี้ คราวนี้ตามหลักการการเขียน Unit test เราต้องคิด Case อื่นด้วย เช่น

  • ถ้ามันส่งค่า Array ที่เป็น Null เข้ามาล่ะจะเป็นยังไง
  • ถ้า Order ที่ส่งเข้ามามันมากกว่าขนาดของ Array
  • ถ้า Array ที่ส่งเข้ามาไม่ Unique ล่ะ
  • ถ้า Order ทีส่งเข้ามาเป็น 0 หรือค่าติดลบล่ะ

ไอ้ถ้าทั้งหมดที่ผมว่าไปทั้งหมดจะกลายเป็น Unit test แต่ละอันไปเลยตามนี้

มี Test แล้วก็เขียน Code

บอกตามตรงโจทย์ง่ายๆแบบนี้ผมเขียนแบบเร็วๆก็มีหลุดไปเหมือนกัน คือ ผมลืมลบตรง order ออก 1 ทำให้มันทำงานผิด แต่พอ Run Test ก็ขึ้นฟ้องเลยว่าไม่ผ่าน พอกลับไปดูก็เจอเลยว่าผิดที่อะไร ตัวอย่าง Code ที่ผมเขียนเป็นแบบนี้

ตัดจบไปดูตอน 3

สำหรับตอนนี้ก็ได้อธิบายวิธีการเขียน Unit test แบบพื้นฐานมากๆ พื้นฐานจนบางคนไม่อยากอ่าน ครับตอนแรกก็อยากจะข้ามตอนนี้ไปเลย แต่ไหนๆจะเขียนละไม่อยากลัดเดี๋ยวคนที่หัดเขียนใหม่ๆจะช็อกตายเอาก็เลยมีตอนนี้เข้ามา คล้ายกับการฝึกวรยุทธ์ต้องฝึกลมปราณให้กล้าแข็งก่อนแล้วค่อยฝึกวิชาขั้นต่อๆไป อย่าไปฝันถึง กบวิเศษที่ต้วนอื้อกิน งูที่ก้วยเจ๋งกิน ที่เป็นทางลัดช่างฝัน แต่ขอให้ฝึกวิชาเหมือน เซียวฟง ผู้ฝึกพื้นฐานจนแข็งแกร่งและใช้วิชา ฝ่ามือปราบมังกร 18 ท่า (เวอร์ชันแรกเรียกแบบนี้ เวอร์ชันใหม่จะเป็น 18 ฝ่ามือพิชิตมังกร) ปราบทั้งยุทธภพ (ไปลองหาอ่านแล้วจะเห็นว่าท่าธรรมดาๆชนะได้ทั้งยุทธภพเป็นยังไง) เหมือนจะนอกเรื่องไปเยอะละ ตอนหน้าจะเอางานจริงที่ทำ แล้วตอนเขียน Unit test เจออะไร แก้ยังไง และ Test โดยไม่ต้องใช้ตัวจริงทำยังไง

"สิ่งที่ผมเขียนขึ้นเป็นเพียงความรู้และความเข้าใจของบุคคลเพียงบุคคลเดียว ดังนั้นอย่าเพิ่งเชื่อในสิ่งที่ผมเขียนและอธิบาย ลองทำความเข้าใจว่ามันเป็นจริงอย่างนั้นไหมและลองหาแหล่งอ้างอิงอื่นๆว่าเขามีแนวคิดอย่างไร เรื่องการ Design และวิธีการใช้งานไม่มีถูกไม่มีผิดมีแต่เหมาะสมกับงานนั้นไหม"

เพลงประกอบการเขียนบทความนี้

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

ref :
https://www.eclipse.org/community/eclipse_newsletter/2017/october/article5.php
https://dzone.com/articles/7-popular-unit-test-naming
https://medium.com/@samueleresca/concepts-of-maintainable-unit-tests-ccb816d93c2b
https://github.com/collab-uniba/socialcde4eclipse/wiki/How-to-import-a-GitHub-project-into-Eclipse
https://howtodoinjava.com/junit5/expected-exception-example/
https://pixabay.com