5 แนวทาง การเขียน Unit Test ให้มีคุณภาพ

26-มิ.ย.-19

คัมภีร์เทพ IT

Unit Test เป็นการตรวจสอบ Software ด้วยการทดสอบส่วนเล็ก ๆ ของ Code เพื่อดูว่ามันทำงานถูกต้องหรือไม่ ดูเหมือน Unit Test สามารถเขียนได้ค่อนข้างง่ายและมันทำงานได้อย่างรวดเร็ว โดยทั่วไปจะทำงานโดยอัตโนมัติเมื่อมีการ Submit Code ไปยัง Repository เพื่อให้มีการตรวจสอบคุณภาพของ Software โดยอัตโนมัติ ก่อนที่จะถูก Deploy บน Production Environment

แต่ในความเป็นจริงแล้ว Unit Test อาจเป็นเรื่องที่ยากในการ Maintain และยากที่จะทำความเข้าใจว่า Code ที่กำลังถูกทดสอบอยู่นั้น ควรจะทำอย่างไรต่อไปหากการทดสอบนั้นเกิดล้มเหลวขึ้นมา ทำให้ Unit Test จะกลายเป็นกระบวนการหนึ่งที่อาจทำให้ทีมทำงานได้ช้าลงและผลการทดสอบนั้น ก็ไม่ได้พิสูจน์อะไรมากนัก ดังนั้น เรามาดู 5 แนวทาง การเขียน Unit Test ให้มีคุณภาพกัน

1. ควรมี 1 Assert ต่อ Test

Unit Test ดูจะเป็นสิ่งที่ใคร ๆ ก็คิดว่ามันเป็นส่วนเล็ก ๆ แต่ในความเป็นจริงมักจะไม่เป็นเช่นนั้น มี Codebases อยู่จำนวนมากที่มีหลายๆ Assert อยู่ใน Test ทำให้ต้อง Setups หลายอย่าง มันทำให้ยากที่จะตัดสินว่ามัน ถูกต้อง หรือไม่ และจะทำให้เกิดความไม่ชัดเจนเมื่อมีการสร้างหรือเพิ่ม Unit Test ใหม่เข้าไป ผลก็คือ จะทำให้ Test นั้นยิ่งมีขนาดใหญ่ขึ้นเรื่อย ๆ:

หลีกเลี่ยงการสร้าง Test ที่มีขนาดใหญ่และประกอบไปด้วย Assert หลายๆ อัน เพราะจะทำให้ชื่อของ Test นั้นไม่มีความหมายที่ชัดเจน

Test = Documentation การมีเพียงแค่ 1 Assert ต่อ Test จะช่วยการันตีได้ว่ามีแค่สิ่งเดียวเท่านั้นที่ถูก Test อยู่ การเลือกชื่อที่สื่อความหมายที่มีความสัมพันธ์กับ Assert จะทำให้เห็นภาพรวมที่สมบูรณ์ของสิ่งที่ test นั้นพยายามทำอยู่ มันง่ายต่อการ Extend และการ Validate ซึ่งโบนัสที่ได้ก็การ Test นั้นจะเป็น Documentation ไปด้วยในตัว

ทำ Unit Test ให้เหมือนกับ Production Code พยายามทำให้มันสามารถ Maintain ได้ อย่าทำอะไรซ้ำๆ ใช้ Method ที่มีขนาดเล็ก, ชัดเจนด้วยชื่อที่สื่อความหมายและทำหน้าที่เพียงอย่างเดียว การใช้ Method ขนาดใหญ่ถือเป็นรูปแบบที่ไม่ควรทำ เพราะยากที่จะทำความเข้าใจ, อาจทำให้เกิด Bug และอาจเกิดปัญหาในเรื่องการ Maintain ดังนั้นควรเขียน Unit Tests ขนาดเล็ก ๆ ที่มีเพียง 1 Assert ต่อ Test:

ใช้ชื่อที่มีความหมายชัดเจนใน 1 Assert ต่อ Test

2. หลีกเลี่ยงการใช้ If-statements ภายใน Test

การใช้ If-statement ภายใน Test นั้นหมายถึง Test นั้นถูกออกแบบมาเพื่อให้ทำสิ่งต่าง ๆ ที่ขึ้นอยู่กับสถานการณ์ หากจำเป็นต้องทดสอบ คุณจะทดสอบการ Test ของคุณได้อย่างไร? แล้วคุณจะทดสอบการ Test ที่ทดสอบการ Test ได้อย่างไร? แล้วมันจะสิ้นสุดตรงจุดไหน?

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

3. Unit Tests เป็นการ “new()” Unit ขึ้นมาภายใต้การ Test

ตอนที่ Unit Test มีขนาดเล็กและจำกัดการ Test เพียงแค่ Method หรือ Class เดียวนั้น การใช้ “new ()” เพื่อสร้าง Object  ของ  Class ที่มี  Method ที่  Unit Test นั้นกำลังจะ Test เป็นเรื่องที่สมเหตุสมผล

แต่เมื่อ  Unit Test นั้นประกอบไปด้วย Object  จากหลาย ๆ Class การใช้  “new()” เพื่อสร้าง Object เหล่านั้น จะทำให้ยากแก่การค้นหาสาเหตุเมื่อ Unit Test นั้น Fail ดังนั้น วิธีที่ดีกว่า คือ การใช้ Framework เช่น Moq หรือ NSubstitute ในการสร้าง Object  เหล่านั้น ให้คงการใช้  “new()” ไว้สร้าง  Object เฉพาะ ที่ Unit Test กำลังจะ Test เท่านั้น ซึ่งจะทำให้ Unit Test มีความชัดเจนมากขึ้น ว่ากำลัง Test Object ของ Class ไหนอยู่:

หลีกเลี่ยงการทำแบบนี้! Class ใดที่ต้องมีการแก้ไข เมื่อการ Test นั้นล้มเหลว? Calendar หรือ Schedule?

อย่าใช้ Dependencies ของ Class ที่คุณกำลัง Test อยู่ แต่แนะนำให้ใช้ “new ()” แทน คุณควรใช้ Frameworks อย่าง Moq หรือ NSubstitute ในการสร้าง Mocks และ Stubs เพื่อแทนที่พวกมัน การทำแบบนี้จะทำให้เรา Focus การ Test นั้นได้ดีกว่า และยังให้ Feedback ที่ดีกว่ากลับมาอีกด้วย

ใช้ Unit ภายใต้ Test (Calendar) ใน Unit Test ของคุณ และ Bug ต้องอยู่ใน Calendar Class

4. ใน Unit Tests ไม่ควรมี Hard-Coded นอกจากมันจะมีความหมายที่เจาะจง

Unit Test น่าจะเป็นสิ่งที่สามารถอธิบายให้ทราบได้ว่า ให้ Input เข้าไป เมื่อ Code กำลังถูก Execute ก็จะได้ผลลัพธ์บางสิ่งออกมา

อย่าทำ Hard-Code ลงไปตรง ๆ ใน Test-Data เพราะมันจะทำให้ยากต่อการทำความเข้าใจ Test สมมติว่า Test ด้านล่างนี้ล้มเหลว, คุณรู้ไหมว่าปัญหาคืออะไร? คุณจะต้อง Debug เพื่อหาว่าอะไรผิด:

หลีกเลี่ยงการทำแบบนี้! มันยากที่จะหาว่าอะไรที่ผิดเมื่อการ Test เกิด Fail ขึ้นมา

เมื่อ Fixture ถูกสร้างขึ้นด้วย Framework การ Test ก็จะมีความชัดเจนมากขึ้น เมื่อการ Test ด้านล่างนี้ Fail แสดงว่ามี Code ที่เกี่ยวกับการตรวจสอบความถูกต้องของหมายเลขโทรศัพท์มีการเปลี่ยนแปลง ทำให้คุณไม่จำเป็นต้อง Debug เพื่อหาคำตอบจากปัญหาที่เกิดขึ้น:

ควรจะกำหนดค่าให้กับ  Properties ที่จะเรากำลังจะทำการ Test เท่านั้น ในกรณีนี้ ควรทำให้เห็นชัดเจนว่า สิ่งที่เกี่ยวกับการตรวจสอบความถูกต้องของหมายเลขโทรศัพท์มีการเปลี่ยนแปลง เมื่อการทดสอบล้มเหลว

5. ไม่มีการเก็บ State ไว้ใน Object/Unit tests

ไม่มีอะไรแย่ไปกว่าการที่ Application ผ่านการ Test ทั้งหมดได้ แต่กลับไปพังใน Production แทน ในสถานการณ์เดียวกันกับที่ Unit Test ได้ Test ผ่านมาแล้ว ซึ่ง Stateful Unit Test สามารถให้เกิดเหตุการณ์แบบนั้นได้:

หลีกเลี่ยงการทำแบบนี้! Stateful unit tests make the outcome of the test debatable.

สมมุติว่า GivenAppointment_ThenErrorOccurs  ถูก Execute ก่อน GivenAppointmentThenAppointmentAppearsInList จะทำให้ผล Test ผิดพลาดสำหรับทั้งกรณีที่ผ่านและไม่ผ่าน

ออกแบบ Unit Tests ให้น่าเชื่อถือ ออกแบบพวกมันให้ผ่านโดยไม่คำนึงถึงลำดับการ Execute ผลลัพธ์ของการทดสอบจะต้องเหมือนกันไม่ว่าจะถูก Execute บ่อยครั้งเพียงก็ตาม

Unit Tests ที่มีขนาดเล็ก, สามารถเชื่อถือได้, รวดเร็ว, สามารถกำหนดกำหนดเป้าหมายได้ และเชื่อถือได้ ล้วนเป็นรากฐานที่แข็งแกร่งของกลยุทธ์การ Test แบบอัตโนมัติที่มีประสิทธิภาพ การใช้ Unit Tests เพียงอย่างเดียวอาจยังไม่เพียงพอ เพราะเป็นการ Test ในส่วนเล็ก ๆ ของ Software เท่านั้น อีกทั้งเนื่องจากการรวมหลาย ๆ Unit สร้างมูลค่าทางธุรกิจ ดังนั้นจึงเป็นสิ่งสำคัญที่คุณจะต้อง Test มันด้วยเช่นกัน คุณจะต้องใช้การ Test ประเภทต่าง ๆ ในส่วนอื่น ๆ ด้วยเพื่อให้มั่นใจในคุณภาพของ Software

ที่มา: https://medium.com/

 

 

รับตำแหน่งงานไอทีใหม่ๆ ด้วยบริการ IT Job Alert

 

อัพเดทบทความจากคนวงในสายไอทีทาง LINE ก่อนใคร
อย่าลืมแอดไลน์ @techstarth เป็นเพื่อนนะคะ

เพิ่มเพื่อน

 

บทความล่าสุด