SOFTWARE DEVELOPMENT | 3 mins read

มาทำ Unittest ด้วยโจทย์ Fizzbuzz กันเถอะ

By Nat Nattaphon on 23 Jun 2021
sennalabs-blog-banner

วันนี้ผมจะชวนทุกท่านมาลองทำ unittest กัน ผมเชื่อว่า มี Developer หลายคนชอบทำ unittest และยังมีอีกหลายคนที่ไม่ชอบทำเช่นกัน แล้ว unittest มันคืออะไรกันนะ ?

unittest คืออะไร

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

1. ทดสอบใส่ตัวเลขสองตัว คือ 5 กับ 8 บวกกัน ผลลัพธ์จะได้คือ 13
2. ทดสอบใส่ตัวเลขติดลบ คือ -1 และ -3 บวกกัน ผลลัพธ์จะได้คือ ไม่สามารถทำการบวกได้ 3
3. ทดสอบใส่ตัวอักขระ A และ B บวกกัน ผลลัพธ์จะได้คือ ไม่สามารถทำการบวกได้

จากตัวอย่างข้างต้น จะเห็นได้ว่า การทดสอบจะประกอบด้วยกัน 3 ส่วน ได้แก่ 

1. Input ของฟังก์ชั่นคืออะไร
2. ทำอะไร
3. Output ของฟังก์ชั่นคืออะไร

ซึ่งผมมักจะเขียนในรูปแบบดังกล่าว แต่จริง ๆ แล้ว ขึ้นอยู่กับการตัดสินใจวางแบบฟอร์มของแต่ละคนว่าจะเขียนในรูปแบบใด สามารถใช้ชื่อตามฟังก์ชันได้เหมือนกัน ไม่ได้ถูกผิด ขึ้นอยู่กับการใช้งานจริงครับ

สำหรับวันนี้ ผมขอเลือกโจทย์ Fizzbuzz มาลองทำกันนะครับ บางคนอาจจะคุ้นเคยกับ Fizzbuzz แล้ว หรือหากไม่คุ้นก็ไม่เป็นไรครับ มาทำความรู้จักกับ Fizzbuzz กัน

โจทย์

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

จากโจทย์ เรามาวางโครงกันก่อนนะครับ

# import lib มาก่อนนะครับ
                import unittest
                
                # สร้างclass ขึ้นมาจะชื่ออะไรก็ได้ครับ
                class FizzBuzz(unittest.TestCase):
                
                
                if __name__ == '__main__':
                    unittest.main()


วิธีการเขียนมักจะเป็นลูปแบบนี้ คือ Write Testcase -> Write Code -> Refector ซึ่งเป็นแนวทางที่ดี แต่เวลาปฏิบัติจริงนั้น มักจะตรงข้ามกัน เนื่องด้วยความไม่ชิน หรือ ไม่ถนัด ซึ่งต้องการการฝึกฝนและเรียนรู้ด้วยครับ โดยวิธีที่ผมทำในงานจริงนั้นก็คือ Write Code -> Refector ->Write Testcase -> Refector ครับ

แต่ในที่นี้ผมขออนุญาตจะใช้แบบนี้แทนคือ Write Testcase -> Write Code -> Refector โดยโจทย์นี้มันจะมีหลาย testcase ให้คิดครับ ผมจะเริ่มจากเคสง่าย ๆ เลย เช่น ใส่เลข 3 ลงไปผลลัพธ์ที่จะออกมาควรเป็น Fizz โดยมีขั้นตอน ดังนี้

  1. เขียนชื่อ testcase
  2. เรียกใช้ฟังก์ชัน calculate_fizzbuzz(3) แล้วให้ส่งค่ากลับมาที่ตัวแปร Actual (ความเป็นจริงจากผลลัพธ์) 
  3. expected (ผลลัพธ์ที่คาดหวัง)
  4. self.assertEqual (actual, expected) ก็จะเป็นการใช้ฟังก์ชันเพื่อเปรียบเทียบ actual == expected หรือไม่ โดยฟังก์ชั่นในการเปรียบเทียบยังมีอีกหลายแบบขึ้นอยู่กับวิธีการเลือกใช้ครับ
import unittest
                
                class FizzBuzz(unittest.TestCase):
                    def test_input_three_should_return_fizz(self):
                        actual = calculate_fizzbuzz(3)
                        expected = 'fizz'
                        self.assertEqual(actual, expected)
                
                def calculate_fizzbuzz(number):
                    pass
                
                if __name__ == '__main__':
                    unittest.main()


หลังจากที่ผมได้ทดลองรัน unittest ด้วยคำสั่ง python <file name> ก็จะได้ผลลัพธ์โดยสังเกตที่ AssertionError: None != ‘fizz’ ซึ่งมันจะผิดอยู่เนื่องด้วยผมไม่ได้เขียนให้ฟังก์ชัน calculate_fizzbuzz นั้นส่งค่าอะไรกลับมา

จาก error ว่า AssertionError: None ≠ 'fizz' ต่อมาก็จะต้องไปทำการแก้ไขโค้ดให้มันทำงานถูกต้อง โดยอาจจะเพิ่ม return ‘fizz' ไปก็ได้เช่นกัน

def calculate_fizzbuzz(number):
                    return ‘fizz’

 

สำหรับท่านที่สนใจสามารถดูตัวอย่าง testcase ที่ได้ทำไป และ ฟังก์ชัน สำหรับใช้เปรียบเทียบครับ

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

หวังเพื่อน ๆ คงจะได้แนวทางไปลองทำ unitest กับโปรแกรมหรือระบบอื่นนะครับ หากสนใจอ่านบทความอื่น ๆ ของผมเพิ่มเติม สามารถอ่านได้ที่ บทความ Deploy FastAPI ไปยัง Heroku ด้วย GitHub Actions ครับ ขอบคุณครับ

Written By
A full-time backend-developer at Sennalabs. Love to learn new skill + tech and a crypto geek.
A full-time backend-developer at Sennalabs. Love to learn new skill + tech and a crypto geek.