TDD & Coverage
เขียนเทสต์ก่อนโค้ด (Red → Green → Refactor) และวัดว่าเทสต์ครอบคลุมโค้ดแค่ไหน
Test-Driven Development (TDD) คือแนวทางเขียน "เทสต์ก่อน" แล้วค่อยเขียนโค้ดให้ผ่าน ฟังดูกลับด้านแต่ช่วยให้คิดชัดว่าโค้ดควรทำอะไร และได้เทสต์ครบเป็นของแถม
วงจร TDD: Red → Green → Refactor
- 🔴 Red — เขียนเทสต์สำหรับสิ่งที่ยังไม่มี รันแล้วต้องแดง (พัง) เพราะยังไม่มีโค้ด
- 🟢 Green — เขียนโค้ดน้อยที่สุดที่ทำให้เทสต์ผ่าน (เขียว)
- 🔵 Refactor — ปรับโค้ดให้สะอาดขึ้น โดยเทสต์ยังเขียวอยู่
# 1. 🔴 เขียนเทสต์ก่อน (ยังไม่มีฟังก์ชัน is_palindrome)
def test_is_palindrome():
assert is_palindrome("racecar") is True
assert is_palindrome("hello") is False
assert is_palindrome("") is True
# 2. 🟢 เขียนโค้ดให้ผ่าน
def is_palindrome(s):
return s == s[::-1]
# 3. 🔵 refactor ถ้าจำเป็น (เทสต์ยังเขียว = ปลอดภัย)การเขียนเทสต์ก่อนบังคับให้คุณคิดว่า "ฟังก์ชันนี้ควรรับอะไร คืนอะไร" ก่อนลงมือ ทำให้ออกแบบ interface ชัดขึ้น และได้เทสต์ครบโดยไม่ต้องมาตามเขียนทีหลัง (ซึ่งมักถูกข้าม)
Coverage — วัดความครอบคลุม
coverage บอกว่าเทสต์ของเรา "แตะ" โค้ดไปกี่เปอร์เซ็นต์ ช่วยหาส่วนที่ยังไม่มีเทสต์
pip install pytest-cov
pytest --cov=myapp # แสดง % coverage
pytest --cov=myapp --cov-report=html # รายงาน HTML ดูบรรทัดที่ยังไม่ถูกเทสต์coverage สูงดี แต่ "แตะโค้ด" ไม่เท่ากับ "ทดสอบถูกต้อง" — อาจรันทุกบรรทัดแต่ไม่ได้ตรวจผลที่สำคัญ เน้นเขียนเทสต์ที่มีความหมาย (เคสปกติ/ขอบ/ผิด) มากกว่าไล่ตัวเลข coverage ให้ครบ 100%
สรุปหัวข้อนี้
- TDD: 🔴 เขียนเทสต์ให้พังก่อน → 🟢 เขียนโค้ดให้ผ่าน → 🔵 refactor
- เขียนเทสต์ก่อนช่วยออกแบบ interface ชัดและได้เทสต์ครบ
- pytest-cov วัด coverage หาส่วนที่ยังไม่มีเทสต์
- coverage สูง ≠ ไม่มี bug — เน้นเทสต์ที่มีความหมาย
1) ใช้ TDD สร้างฟังก์ชัน count_vowels(s): เขียนเทสต์ให้พังก่อน แล้วค่อยเขียนโค้ด 2) เพิ่มเคสขอบ (string ว่าง, ตัวพิมพ์ใหญ่) แล้วทำให้ผ่าน 3) รัน pytest --cov ดู % 4) อธิบายว่าทำไม 100% coverage ไม่การันตีว่าไม่มี bug