แนวคิดการเขียนโค้ดเพื่อทดสอบโค้ดของเรานั้นอาจจะไม่ใช่แนวคิดที่ใหม่แต่อย่างใด แต่การเขียนโค้ดเพื่อทดสอบ “ก่อน” เขียนโค้ด (Test-Driven Development หรือ TDD) ยังคงเป็นเรื่องใหม่สำหรับหลายๆ คน ปัญหาหลักอีกอย่างหนึ่งก็คงเป็นเพราะเราไม่รู้ว่าจะเริ่มต้นเขียนอย่างไร เราจะมาเริ่มต้นเขียนโค้ดเพื่อทดสอบ และเขียน “ก่อน” ที่เราจะเขียนโค้ดเพื่อทำงานจริงๆ กันโดยใช้ Python ในบทความนี้
สำหรับคนที่ใช้ Mac หรือพวก Linux ต่างๆ Python จะติดตั้งมาให้เรียบร้อยแล้ว ไม่ต้องลงอะไรเพิ่ม ส่วนคนที่ใช้ Windows ต้องไปโหลดมาก่อนนะครับที่ Python Releases for Windows
สมมุติว่าเราต้องการที่จะกรองตัวเลขออกมาจากข้อความใดๆ เช่น ข้อความ “aa1bb2cc3dd” ผลลัพธ์ที่ได้คือ 123 ก่อนอื่นให้เราสร้างไฟล์ filter_string.py
ขึ้นมา แล้วเขียนตามนี้
import unittest class FilterStringTest(unittest.TestCase): def test_fail(self): self.assertTrue(False) if __name__ == '__main__': unittest.main()
บรรทัดแรกเป็นการนำโมดูล Unit Test Framework ชื่อ unittest เข้ามาใช้ โมดูลนี้จะช่วยให้เราเขียนโค้ดทดสอบได้สะดวกมากขึ้น เราแค่เขียนโค้ดทดสอบไปในแต่ละกรณี แล้วพอสั่งคำสั่ง python
กับไฟล์นี้ ตัวโมดูลจะเข้าไปอ่านโค้ดทดสอบของเราแล้วก็จะนำไปประมวลผลให้เรา เรานั่งรอดูผลลัพธ์เท่านั้น
บรรทัดในส่วนของ class ให้เรามองเป็นชุดทดสอบของเรา และภายในชุดทดสอบก็จะมีหลายๆ ตัวทดสอบ ในกรณีนี้เรามีชุดทดสอบของโปรแกรมกรองตัวเลขอยู่ชุดหนึ่ง แล้วก็มีตัวทดสอบ fail อยู่ข้างใน
ส่วนบรรทัด if __name__ == '__main__':
เป็นทริกครับ ค่า __name__
ในโมดูลจะถูกเซตตามวิธีที่เราใช้งาน ถ้าเรา import เข้ามา ค่า __name__
จะเป็นชื่อโมดูลนั้นๆ แต่ถ้าเราสั่งคำสั่งกับโมดูลนั้นโดยตรง ค่า __name__
จะเป็น __main__
ครับ ซึ่งปกติเราจะเขียนไฟล์โค้ดสำหรับทดสอบแยกไว้ แล้วก็สั่งคำสั่งแยกจากโค้ดที่ใช้ทำงานจริง ดังนั้นเวลาที่เราอยากสั่งคำสั่งกับโค้ดทดสอบ ให้เราสั่งงานกับโค้ดนั้นโดยตรงได้เลย
ให้เราลองสั่ง python filter_string.py
เลย เราควรจะได้ผลลัพธ์แบบนี้
➜ ~ python filter_string.py F ====================================================================== FAIL: test_fail (__main__.FilterStringTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "filter_string.py", line 6, in test_fail self.assertTrue(False) AssertionError: False is not true ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1)
เราได้ F ออกมาแปลว่าตัวทดสอบของเรา fail ครับ ณ จุดนี้เราก็พร้อมที่จะเริ่มต้นเขียนชุดทดสอบของเราจริงๆ กันแล้วครับ 🙂
เรากลับไปเขียนตัวทดสอบของเราให้มันดีๆ กันดีกว่า ผมขอเปลี่ยนตัวทดสอบ test_fail
เป็น test_input_containing_only_digits_should_get_only_digits
ตามนี้
import unittest class FilterStringTest(unittest.TestCase): def test_input_containing_only_digits_should_get_only_digits(self): result = filter_string('123') self.assertEqual(result, 123) if __name__ == '__main__': unittest.main()
ถ้าเราสั่ง python filter_string.py
เราควรจะได้แบบนี้
➜ ~ python filter_string.py E ====================================================================== ERROR: test_input_containing_only_digits_should_get_only_digits (__main__.FilterStringTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "filter_string.py", line 6, in test_input_containing_only_digits_should_get_only_digits result = filter_string('123') NameError: global name 'filter_string' is not defined ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (errors=1)
ได้ E แปละว่า error ครับ เรายังไม่มีฟังก์ชั่น filter_string
เลย ต่อไปเราก็ไปสร้างฟังก์ชั่นนี้ซะ ผมขอสร้างไว้ที่ไฟล์เดียวกันเลยนะครับ ตามนี้
import unittest class FilterStringTest(unittest.TestCase): def test_input_containing_only_digits_should_get_only_digits(self): result = filter_string('123') self.assertEqual(result, 123) def filter_string(text): return 123 if __name__ == '__main__': unittest.main()
พอเราสั่ง python filter_string.py
อีกที เราจะได้
➜ ~ python filter_string.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
แบบนี้แปลว่าโค้ดทดสอบของเราผ่าน โค้ดทำงานได้ตามที่เราคาดหมายไว้ครับ
ที่ผมเน้นว่าโค้ดทำงานได้ตามที่เราคาดหมายไว้ หมายความว่า เราเขียนชุดทดสอบไว้แค่ไหน เราก็ต้องคาดหมายไว้ว่าโค้ดจะทำงานได้แค่นั้นครับ ไม่ได้หมายความโค้ดของผมสมบูรณ์เสร็จสิ้นแล้ว เช่นในกรณีนี้ผมมีตัวทดสอบแค่ไว้ทดสอบว่า ถ้าใส่ข้อความ “123” ผลลัพธ์ที่ได้ก็คือ 123 แค่นั้น
ที่เหลือผมขอให้เป็นงานของผู้ที่หลงเข้ามาอ่านบทความนี้นะครับ ช่วยทำโปรแกรมของผมให้สมบูรณ์ที
ขอบคุณครับ 🙂