แนวคิดการเขียนโค้ดเพื่อทดสอบโค้ดของเรานั้นอาจจะไม่ใช่แนวคิดที่ใหม่แต่อย่างใด แต่การเขียนโค้ดเพื่อทดสอบ “ก่อน” เขียนโค้ด (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 แค่นั้น
ที่เหลือผมขอให้เป็นงานของผู้ที่หลงเข้ามาอ่านบทความนี้นะครับ ช่วยทำโปรแกรมของผมให้สมบูรณ์ที
ขอบคุณครับ 🙂