目次
1. Python unittest คืออะไร?
unittest เป็นเฟรมเวิร์กการทดสอบหน่วยที่รวมอยู่ในไลบรารีมาตรฐานของ Python ซึ่งเป็นเครื่องมือสำคัญในการรับประกันคุณภาพของโค้ด ทำให้ผู้พัฒนาสามารถทดสอบแต่ละส่วนของโค้ดได้อย่างอิสระและช่วยให้ค้นหา bug ได้ตั้งแต่เนิ่นๆ นอกจากนี้ยังช่วยตรวจสอบว่าการเปลี่ยนแปลงโค้ดในระหว่างการพัฒนาอย่างต่อเนื่องไม่ได้ทำลายฟังก์ชันที่มีอยู่ความสำคัญของการทดสอบหน่วย
เมื่อโค้ดซับซ้อนมากขึ้น การตรวจสอบว่าต่างส่วนทำงานร่วมกันอย่างถูกต้องนั้นยากขึ้น การนำการทดสอบหน่วยมาใช้ทำให้ป้องกันบั๊กที่ไม่คาดคิดจากการเปลี่ยนแปลงเล็กน้อยได้ง่ายขึ้นและช่วยรักษาเสถียรภาพของโปรแกรมโดยรวมAd
2. วิธีการใช้ unittest เบื้องต้น
unittestพื้นฐานคือการสร้างคลาสที่สืบทอดจาก unittest.TestCase แล้วกำหนดเมธอดทดสอบภายในคลาสนั้น ในเมธอดทดสอบจะใช้เมธอด assertion เช่น assertEqual() เพื่อเปรียบเทียบผลลัพธ์ที่คาดหวังกับผลลัพธ์จริงตัวอย่างการทดสอบพื้นฐาน
โค้ดต่อไปนี้เป็นตัวอย่างง่าย ๆ ที่ทดสอบฟังก์ชันadd(a, b)import
# โค้ดที่ต้องทดสอบ
def add(a, b):
return a + b
# คลาสทดสอบ
class TestAddFunction(unittest.TestCase):
def test_add_integers(self):
result = add(2, 3)
self.assertEqual(result, 5)
if __name__ == '__main__':
unittest.main()ในโค้ดนี้ทำการทดสอบว่า ฟังก์ชัน add() ทำงานอย่างถูกต้องหรือไม่ เมธอด assertEqual() ตรวจสอบว่าค่าที่คาดหวังและผลลัพธ์จริงเท่ากัน วิธีนี้ช่วยยืนยันว่าฟังก์ชันทำงานถูกต้องในหลายกรณีการขยายการทดสอบ
สามารถใช้เมธอดทดสอบหลาย ๆ ตัวเพื่อทดสอบการทำงานของฟังก์ชันกับอินพุตที่หลากหลาย เช่น การทดสอบจำนวนทศนิยมหรือการต่อสตริงdef test_add_floats(self):
result = add(2.5, 3.5)
self.assertAlmostEqual(result, 6.0, places=2)
def test_add_strings(self):
result = add("Hello, ", "World!")
self.assertEqual(result, "Hello, World!")ด้วยวิธีนี้ การทดสอบการทำงานของฟังก์ชันกับประเภทข้อมูลที่ต่างกันก็ช่วยยืนยันว่าฟังก์ชันทำงานถูกต้องในสถานการณ์ต่าง ๆ3. วิธีการใช้ setUp() และ tearDown()
เพื่อให้ดำเนินการบางอย่างโดยอัตโนมัติก่อนและหลังการทดสอบ ให้ใช้เมธอดsetUp() และ tearDown() ซึ่งจะทำให้สามารถเตรียมการที่จำเป็นก่อนการรันเทสต์และทำความสะอาดหลังจากเทสต์เสร็จสิ้นได้ตัวอย่าง setUp()
setUp() เมธอดเป็นเมธอดที่เรียกใช้เสมอก่อนแต่ละเมธอดทดสอบ ทำให้สามารถรวมขั้นตอนการเริ่มต้นที่ใช้ร่วมกันได้def setUp(self):
self.temp_value = 42ตัวอย่าง tearDown()
tearDown() เมธอดจะทำงานหลังจากแต่ละเมธอดทดสอบ เพื่อทำการทำความสะอาดหรือปล่อยทรัพยากร เช่น การตัดการเชื่อมต่อฐานข้อมูลหรือการลบไฟล์ชั่วคราวdef tearDown(self):
self.temp_value = Noneด้วยวิธีนี้จะช่วยลดความซ้ำซ้อนของโค้ดทดสอบและรักษาโค้ดให้สะอาดยิ่งขึ้นAd
4. การทดสอบความขึ้นอยู่ด้วยการใช้ Mock
เมื่อโค้ดที่ต้องการทดสอบพึ่งพาแหล่งข้อมูลภายนอก(ฐานข้อมูล、API เป็นต้น)ในกรณีที่พึ่งพานั้นถูกแทนที่ด้วย mock จะช่วยปรับปรุงความเร็วในการรันเทสต์และทำให้เทสต์สามารถคาดการณ์ได้ หากใช้โมดูลunittest.mock ของ Python ก็สามารถทำได้อย่างง่ายดายตัวอย่างการใช้ Mock
ในโค้ดต่อไปนี้ ฟังก์ชันที่ใช้เวลานานtime_consuming_function() ถูกแทนที่ด้วย mockfrom unittest.mock import patch
class TestAddFunction(unittest.TestCase):
@patch('my_module.time_consuming_function')
def test_add_with_mock(self, mock_func):
mock_func.return_value = 0
result = add(2, 3)
self.assertEqual(result, 5)ในตัวอย่างนี้ เราใช้ mock เพื่อทำการทดสอบโดยไม่เรียก time_consuming_function ซึ่งช่วยลดเวลาในการทดสอบในขณะเดียวกันก็ได้ผลลัพธ์ที่แม่นยำ
5. การจัดการข้อยกเว้นและการอ้างอิงแบบกำหนดเอง
unittest ในสามารถทดสอบการจัดการข้อยกเว้นได้เช่นกัน ตัวอย่างเช่น เพื่อตรวจสอบว่าข้อยกเว้นเกิดขึ้นอย่างถูกต้องในสถานการณ์เฉพาะ ให้ใช้ assertRaises() เพื่อทำเช่นนั้นการทดสอบการจัดการข้อยกเว้น
ในตัวอย่างต่อไปนี้ จะตรวจสอบว่ามีการเกิดZeroDivisionErrordef test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
divide(1, 0)โค้ดนี้ทดสอบว่าเมื่อเรียก divide(1, 0) จะเกิด ZeroDivisionErrorการสร้างการอ้างอิงแบบกำหนดเอง
ในกรณีที่การอ้างอิงมาตรฐานไม่สามารถรองรับได้ สามารถสร้างเมธอดการอ้างอิงแบบกำหนดเองของคุณเองได้def assertIsPositive(self, value):
self.assertTrue(value > 0, f'{value} is not positive')โดยใช้การอ้างอิงแบบกำหนดเอง คุณสามารถรองรับสถานการณ์การทดสอบที่เฉพาะเจาะจงมากขึ้นAd
6. ฟีเจอร์การค้นหาเทสต์ของ unittest
การใช้ฟีเจอร์การค้นหาเทสต์ของunittest จะทำให้คุณสามารถค้นหาและรันไฟล์เทสต์ทั้งหมดในโปรเจกต์โดยอัตโนมัติ ฟีเจอร์นี้มีประโยชน์อย่างยิ่งโดยเฉพาะในโปรเจกต์ขนาดใหญ่วิธีใช้การค้นหาเทสต์
เพื่อรันการค้นหาเทสต์ ให้ใช้คำสั่งต่อไปนี้python -m unittest discoverด้วยคำสั่งนี้ จะทำการรันไฟล์ test_*.py ทั้งหมดในไดเรกทอรีที่ระบุ หากต้องการระบุไฟล์หรือไดเรกทอรี ให้ใช้ตัวเลือกดังต่อไปนี้python -m unittest discover -s tests -p "test_*.py"ด้วยฟีเจอร์นี้ คุณสามารถประหยัดความยุ่งยากในการระบุไฟล์เทสต์แต่ละไฟล์ได้ และสามารถจัดการเทสต์อย่างมีประสิทธิภาพแม้ในโปรเจกต์ขนาดใหญ่
Ad
7. เคล็ดลับการเพิ่มประสิทธิภาพด้วย unittest
หากความเร็วในการรันเทสต์ช้า ประสิทธิภาพการพัฒนาจะลดลง ที่นี่เราจะแนะนำเคล็ดลับบางอย่างเพื่อปรับปรุงประสิทธิภาพของเทสต์โดยใช้unittest。เพิ่มประสิทธิภาพไฟล์ I/O
เทสต์ที่ต้องอ่านเขียนไฟล์สามารถเร่งความเร็วได้โดยประมวลผลในหน่วยความจำ ใช้StringIO เพื่อสร้างอ็อบเจ็กต์ที่ทำหน้าที่เหมือนไฟล์ในหน่วยความจำ ทำให้หลีกเลี่ยงการทำ I/O กับดิสก์。from io import StringIO
class TestFileOperations(unittest.TestCase):
def test_write_to_memory(self):
output = StringIO()
output.write('Hello, World!')
self.assertEqual(output.getvalue(), 'Hello, World!')ด้วยวิธีนี้ แม้เทสต์ที่ต้องเข้าถึงไฟล์ก็สามารถปรับปรุงความเร็วของเทสต์ได้อย่างมาก。ใช้ Mock
เพื่อจำกัดการเข้าถึงทรัพยากรภายนอก เราสามารถใช้ Mock เพื่อเร่งความเร็วของเทสต์ได้ ซึ่งช่วยหลีกเลี่ยงความล่าช้าจากเครือข่ายฐานข้อมูลและลดเวลาการรันเทสต์ ตัวอย่างต่อไปนี้จะแทนการเรียก API ด้วย Mock。from unittest.mock import MagicMock
class TestApiCall(unittest.TestCase):
def test_api_response(self):
mock_api = MagicMock(return_value={'status': 'success'})
response = mock_api()
self.assertEqual(response['status'], 'success')ด้วยวิธีนี้ การทดสอบฟังก์ชันโดยไม่พึ่งพาทรัพยากรภายนอกจะทำให้สร้างสภาพแวดล้อมการทดสอบที่เร็วขึ้นและเสถียรยิ่งขึ้น。Ad
8. สรุปและขั้นตอนต่อไป
ในบทความนี้ เราได้อธิบายอย่างกว้างขวางตั้งแต่พื้นฐานของการทดสอบหน่วยด้วยunittest ของ Python การใช้การตั้งค่าและการทำความสะอาด การทดสอบความขึ้นอยู่ด้วย mock ไปจนถึงเทคนิคการปรับปรุงประสิทธิภาพของการทดสอบสรุปประเด็นสำคัญ
- วิธีการใช้พื้นฐาน: สืบทอดจาก
unittest.TestCaseและใช้เมธอด assertion เพื่อสร้างการทดสอบ - setUp() / tearDown(): การจัดการกระบวนการทั่วไปก่อนและหลังการทดสอบช่วยเพิ่มการนำโค้ดกลับมาใช้ใหม่และความอ่านง่ายของโค้ด
- การใช้ mock: สามารถทดสอบฟังก์ชันโดยไม่ต้องพึ่งพาแหล่งข้อมูลภายนอก ทำให้ประสิทธิภาพของการทดสอบเพิ่มขึ้นอย่างมาก
- การค้นพบการทดสอบ (Test Discovery): ฟีเจอร์ที่สะดวกช่วยให้การจัดการการทดสอบในโครงการขนาดใหญ่ง่ายขึ้น
- เทคนิคการปรับปรุงประสิทธิภาพ: การประมวลผลในหน่วยความจำและการใช้ mock สามารถลดเวลาการรันของการทดสอบได้
ขั้นตอนต่อไป
เมื่อคุณเชี่ยวชาญพื้นฐานของunittest แล้ว ลองท้าทายวิธีการทดสอบขั้นสูงต่อไป เช่น การทดสอบแบบพารามิเตอร์ที่สามารถทดสอบข้อมูลหลายชุดพร้อมกัน หรือใช้เครื่องมือ coverage เพื่อตรวจสอบขอบเขตการทดสอบของโค้ด ซึ่งช่วยเสริมกลยุทธ์การทดสอบของโครงการทั้งหมด นอกจากนี้ การมองหาเฟรมเวิร์กการทดสอบอื่น ๆ เช่น pytest ก็เป็นทางเลือกที่ดีเพื่อขยายตัวเลือกตามการใช้งาน
การทดสอบเป็นส่วนสำคัญของการพัฒนา เพื่อค้นหา bug ตั้งแต่เนิ่นๆ และรักษาคุณภาพของโค้ด ควรนำการทดสอบเข้ามาใช้โดยกระตือรือร้น


