สคริปต์ Commit Message ของผมมี 8 Assertions ใน Selftest แต่ไม่มีอันไหนแตะโค้ดที่พังจริงเลย
บทเรียนจากการตรวจโค้ดเทสต์ของตัวเอง เมื่อพบว่าสคริปต์ generate ข้อความ commit ผ่าน Claude AI มีการเขียนบล็อก selftest ไว้ครบแต่ไม่ได้ทดสอบฟังก์ชันส่วนที่อาจจะล้มเหลวได้จริง

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- พบปัญหาช่องโหว่ใน test coverage ของสคริปต์ git_commit.py หลังจากศึกษาแนวทางจากโพสต์ยอดฮิต
- ไฟล์พี่น้องอย่าง publish_devto.py และ server.py มีการเขียน stub และทดสอบเคสพังไว้อย่างดี
- git_commit.py ทำงานแบบ top-level script ทำให้ไม่สามารถเรียกฟังก์ชันมาสลับ dependency ได้ง่าย
- แนวทางแก้ไขคือต้อง refactor โค้ดให้ห่อหุ้มด้วยฟังก์ชันและจำลอง subprocess.check_output เช่นเดียวกับไฟล์อื่น
ในโปรเจกต์นี้มีไฟล์ทั้งหมดสามไฟล์ที่เรียกใช้งานเครือข่ายภายนอกหรือรัน subprocess ซึ่งมีความเสี่ยงที่จะเกิดข้อผิดพลาดในหลากหลายรูปแบบ ได้แก่ publish_devto.py, server.py และ git_commit.py โดยสองไฟล์แรกมีบล็อก --selftest ที่ทำหน้าที่ stub การเรียกใช้งานที่มีความเสี่ยงและจำลองกิ่งก้านสาขาที่เกิดความผิดพลาดไว้เรียบร้อยแล้ว
ทว่า git_commit.py กลับไม่มีการเขียนทดสอบในลักษณะเดียวกัน ซึ่งผู้พัฒนาเพิ่งสังเกตเห็นหลังจากเริ่มรู้สึกสงสัยใน test coverage ของตัวเอง หลังจากได้ไปอ่านโพสต์ยอดนิยมเกี่ยวกับการนับจำนวน assertions ในชุดทดสอบแล้วพบสิ่งที่ไม่น่าพึงพอใจ
สคริปต์ git_commit.py ทำหน้าที่อ่าน staged diff แล้วเรียกคำสั่ง claude -p เพื่อแปลงออกมาเป็น commit message โดยมี exit paths แยกย่อยถึง 5 เส้นทาง ซึ่งทั้งหมดถูกสร้างขึ้นเพื่อคอยป้องกันข้อผิดพลาดจริงที่ผู้พัฒนาเคยเจอมาแล้วในโปรเจกต์นี้
หากลองเทียบกับบล็อก --selftest ของอีกสองไฟล์ที่เขียนขึ้นในสัปดาห์เดียวกัน จะเห็นว่าถูกตั้งมาตรฐานไว้ต่างกันอย่างสิ้นเชิง เช่นใน publish_devto.py มีการเขียน stub ครอบ urllib.request.urlopen เพื่อจำลองเคสพัง:
def _fake_url_error(req, timeout=30): raise urllib.error.URLError("timed out")

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
การทำ Test Coverage และการเขียน Self-Test ให้กับสคริปต์ขนาดเล็ก มักเป็นจุดที่นักพัฒนาหลายคนมองข้ามเพราะคิดว่าเป็นเพียงโค้ดเครื่องมือช่วยทำงานชั่วคราว (Utility Script) แต่ในความเป็นจริง สคริปต์ที่เชื่อมต่อกับภายนอกอย่าง AI CLI หรือระบบ API ภายนอก มักมีความเปราะบางสูง การจำลองข้อผิดพลาดด้วย Stub และ Mock จึงช่วยป้องกันบั๊กที่คาดไม่ถึงได้อย่างมีประสิทธิภาพ ดังเช่นกรณีศึกษาจากไฟล์ publish_devto.py และ server.py
ปัญหาหลักของ git_commit.py คือโครงสร้างโค้ดที่มีความยาวราว 20 บรรทัดและรันแบบ top-level script ทั้งหมด ตั้งแต่อ่าน diff, เรียก claude -p ไปจนถึงพิมพ์ผลลัพธ์ โดยมีเพียงเงื่อนไขดัก if "--selftest" in sys.argv: ... raise SystemExit(0) อยู่ด้านบนสุดเท่านั้น ทำให้ไม่มีฟังก์ชันให้เรียกใช้งานพร้อมกับสลับค่า subprocess.check_output ได้โดยตรง
แนวทางแก้ไขที่ตรงจุดจึงแบ่งออกเป็นสองขั้นตอน ได้แก่ การย้ายตรรกะการอ่าน diff และสร้างข้อความให้อยู่ในรูปแบบฟังก์ชันเช่นเดียวกับไฟล์พี่น้องทั้งสอง และนำวิธี stub-and-assert แบบเดียวกับที่ใช้ใน already_published(), _gh() และ _dev() มาประยุกต์ใช้ต่อไป ซึ่งเป็นแนวทางที่ปรากฏอยู่ในโปรเจกต์เดียวกันนี้อยู่แล้ว
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น