ปรับมุมมอง ปูแนวคิดเพื่อการทำ Unittest ให้ง่ายมากขึ้น

Paranee Apiromsanee
te<h @TDG
Published in
2 min readOct 3, 2022

จุดประสงค์ของการเขียน unittest คือการทดสอบโค้ดสั้นๆว่าถูกต้องและได้ผลลัพธ์ตามที่ต้องการหรือไม่

Unittest Failed

Unit = หน่วย

Test = ทดสอบ

รวมกันคือการทดสอบสิ่งหนึ่งหรือหน่วยหนึ่ง ซึ่งในทาง coding คือการทดสอบโค้ดชุดหนึ่งนั่นเอง

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

ซึ่งในอุดมคติการเขียนโค้ดที่ดีควรจะกระชับ มีจุดประสงค์เดียวต่อหนึ่งการทำงานอย่างชัดเจน (และสามารถใช้ซ้ำ reusable ได้ ซึ่งจุดนี้เราจะไม่พูดถึงในบทความนี้ ถือว่าเป็นผลพลอยได้ และเดาว่าทุกคนเขียนฟังก์ชันแยกการทำงานนะ 😂) ในที่นี้ขอเรียกว่าฟังก์ชันเพื่อให้ง่ายต่อการอธิบาย (= method เป็นอีกชื่อเรียกนึงของฟังก์ชัน)

Example Unittest

ทีนี้การที่เราจะสร้าง unittest ได้ สิ่งที่สำคัญที่สุดคือต้องเข้าใจจุดประสงค์ของฟังก์ชันนั้นๆก่อนว่าผลลัพธ์หลังจากการ run โค้ดคืออะไร

วิธีนี้สามารถใช้ได้ทั้งในกรณีที่มีโค้ดอยู่แล้ว (คือเข้าไปทำงานใหม่แล้วคนเก่าลาออก เราต้องมานั่งเก็บ unittest ให้) หรือยังไม่มีโค้ดก็สามารถทำได้เช่นกัน

สำหรับแนวทางการเริ่มต้นในการเขียน unittest คือ requirement จาก PO จะทำให้เรารู้จุดประสงค์ของโค้ดได้ชัดเจนขึ้น

เช่น

Requirement ของ PO บอกว่าอยากให้ text จุดนี้เป็น format จำนวนเงินเต็มบวกในรูปแบบ ฿xxx,xxx,xxx.00

(อธิบายคือ หลักเศษให้มี 00 สองหลักในกรณีที่ไม่มีเศษ ถ้ามีให้ใส่ตามข้อมูลที่ได้มา

มี , ทุกๆ 3 หลัก ขึ้นอยู่กับข้อมูล

รองรับถึงร้อยล้าน และมีสัญลักษณ์ ฿ ด้านหน้า)

ตัวอย่างเช่น

฿123,456,789.00

฿12.55

฿1,234.01

฿0.00

(ทั้งนี้ขอละไว้เพราะหลายเคสมาก)

เราจะเริ่มมีไอเดียขึ้นมาว่าโค้ดที่เราจะเขียนออกมาจะมีผลลัพธ์อะไรบ้าง

ดังนั้นจุดประสงค์ของ unittest คือการเขียนโค้ดเพื่อตรวจสอบว่าหลังจากที่รันโค้ดที่เป็น logic ของเราแล้วนั้นจะได้ผลลัพธ์ดังที่ต้องการหรือไม่

ต่อมาให้เราเรียบเรียงลำดับของการทำงานของ unittest ในรูปแบบโค้ด

  1. สร้าง class (assume ว่ามี lib ที่ gradle เรียบร้อยแล้ว)
  2. Given ข้อมูลจำลองที่จะเป็น input เพื่อทดสอบชุดโค้ด logic ของเรา
  3. Expect สร้าง expect result คือผลลัพธ์ที่คาดหวัง
  4. Actual สร้าง actual result คือผลลัพธ์ที่ได้ออกมาจริงๆ ซึ่งผลลัพธ์นี้จะได้จากการเรียกฟังก์ชัน logic โค้ดของเรา
  5. Then เทียบค่า expect กับ actual ว่าผลลัพธ์ตรงกันหรือไม่

ฟอร์แมตคร่าวๆที่แนะนำให้ใช้ comment เพื่อไกด์ตัวเองก่อนลงมือเขียนตามสไตล์ของจขบค.

// Given

val given = 1234.015 // คือค่าของข้อมูลที่จะใส่ลงไปในฟังก์ชัน

// Expect

val expect = “฿1,234.02” // ผลลัพธ์ที่ต้องการ

// Actual

val actual = convertNumberToBahtFormat(given) // คือส่วนของการเรียกโค้ด logic ของเรา (ซึ่งอาจจะมีหรือไม่มีโค้ดชุดนี้อยู่ก็ได้ ขึ้นอยู่กับเทคนิคการเขียน ในบทความนี้จะขอข้ามส่วนนี้ไปก่อน)

// Then

assertEquals(expect, actual) // คำสั่ง assertEquals เป็นคำสั่งเพื่อใช้เทียบค่าสองค่าที่มีค่าเท่ากัน อ่านง่ายๆว่า actual = expect

ซึ่งถ้าเทิร์นโปรแล้ว จะใช้รูปแบบอื่นก็ไม่ติด เช่น

// Given คือค่าของข้อมูลที่จะใส่ลงไปในฟังก์ชัน

// When คือส่วนของการเรียกโค้ด logic ของเรา (ซึ่งอาจจะมีหรือไม่มีโค้ดชุดนี้อยู่ก็ได้ ขึ้นอยู่กับเทคนิคการเขียน ในบทความนี้จะขอข้ามส่วนนี้ไปก่อน)

// Then ตรวจสอบผลลัพธ์ที่คาดหวังกับผลลัพธ์ที่ได้จริง actual = expect

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

เมื่อเรากด run unittest จะขึ้นสัญลักษณ์เครื่องหมายถูกสีเขียว (หรืออาจจะวงกลม หรือรูปอื่นๆ สำคัญที่สีเขียวนี่แหละ) แสดงว่าผ่าน

Unittest Passed

โค้ด logic ของเราได้ผลตามที่คาดหวังไว้ หรือ meet requirement นั่นเอง

ซึ่งการเขียน unittest นั่นจะมีได้หลายฟังก์ชันต่อ 1 logic เพราะในโลกของความเป็นจริงค่าที่ given เข้าไปในการใช้งานจริงมีหลายรูปแบบ

เราก็จะมี unittest ที่เรียกฟังก์ชั่น logic เดิมๆหลายๆฟังก์ชันที่มี given และ expect แตกต่างกันไปตาม requirement

อย่างเช่นในตัวอย่าง จะต้องมีฟังก์ชั่น unittest เพื่อ test ค่าข้อมูลในรูปแบบอื่นๆ (ถามว่าจะรู้ได้ยังไงว่าต้องมีค่าอะไรบ้าง สามารถติดตามได้ในบทความต่อๆไป)

Given ที่เราจะต้องนำมาเทสคือ

123456789.00

12.55

1234.01

0.00

อย่างน้อยๆควรจะมีค่าที่ requirement กำหนดมา หรือค่ากว้างๆ เช่น ขอบบนสุด ขอบล่างสุด ติดลบ เป็นต้น ถ้านึกไม่ออกให้ขอความช่วยเหลือจาก QA

ซึ่ง unittest ที่มีคุณภาพและประสิทธิภาพจะเปรียบเสมือนการยิงปืนนัดเดียวได้นกสองตัว

หมายถึงการใส่ input หรือ given เพียงค่าเดียวและสามารถเทสได้หลายเคสในครั้งเดียว ซึ่งข้อดีคือ build time จะเร็วขึ้น (ซึ่งถ้า unittest น้อยๆก็ไม่ค่อยเห็นผล แต่ถ้ามีเป็นพันขึ้นมาล่ะก็…)

มันคือการเลือก input อย่างชาญฉลาดนั่นเอง (ตรงจุดนี้เราสามารถปรึกษา QA ได้)

แต่ในโลกของความเป็นจริงคือ ขอให้มีก่อนเถอะ 😂😂😂😂

สุดท้ายนี้ที่อยากฝากคืออย่าลืมจุดประสงค์ ให้พยายามทำความเข้าใจว่ากำลังจะทำอะไร ต้องใช้อะไร มีสิ่งนี้ไว้ทำไม

--

--