Skip to content

การทำ TDD สำหรับ Python

ภาษาโปรแกรมสมัยใหม่ (หมายถึงยุคนี้ซึ่งอาจจะมีอายุ 10-20 ปีแล้วก็ได้) บางภาษาทุกวันนี้มักจะ built-in ไลบรารี่ unit testing framework มาให้อยู่แล้วอย่างเช่น Golang Python เองก็มีมาให้ด้วยเหมือนกัน

โพสต์นี้ผมอธิบายเป็น step ตามนี้ละกันครับ

1. เตรียมเครื่องมือที่จำเป็นก่อน

เนื่องจากผมใช้ Ubuntu ฉะนั้นเครื่องไม้เครื่องต่างๆ จะเป็นทางฝั่งลินุกซ์นะครับ OS อื่นๆ ก็หามาติดตั้งเองครับไม่น่ายากสำหรับ developer อย่างเรา

1.1 ติดตั้ง Editor/IDE

มีให้เลือกใช้เยอะแยะตั้งแต่ editor ธรรมดาและ IDE ซึ่งมีเครื่องไม้เครื่องมีในการพัฒนามาให้ตั้งแต่เริ่มต้น เลือกใช้กันตามสะดวก ใครใช้ editor ก็เลือกที่ highlight โค้ดได้ก็ดีครับจะได้ดูง่ายๆ

ในตัวอย่างนี้ผมจะใช้ editor ธรรมดาคู่กับการรันคำสั่งจาก terminal ครับ

1.2 ติดตั้ง Python

สำหรับ Python Ubuntu มีให้อยู่แล้วไม่ต้องติดตั้งเพิ่มอีก ใช้ Python 2 หรือ Python 3 ก็ได้ครับ

ถ้าใช้ Ubuntu อยู่ ข้ามไปเลยก็ได้ครับ

1.3 [Optional] ติดตั้ง Pylint

ภาษาโปรแกรมแต่ละภาษาจะมีมาตรฐานการเขียนของมันอยู่ เช่น การย่อหน้า, การตั้งชื่อตัวแปร, การเว้นวรรค-บรรทัด, การเขียน document ประกอบคลาส, เมธอด

Python เองก็มีเช่นกันครับ Pylint นี้จะช่วยตรวจสอบว่าโค้ดของเราถูก format มาตรฐานตามที่ develop ทั่วไป (โครงการ Open source) เขายอมรับหรือไม่, ช่วยหาตัวแปรที่ไม่ถูกใช้, ดูว่าคลาส/ฟังก์ชันของเราซับซ้อนขนาดไหน แนะนำว่าควรใช้ครับโค้ดของเราจะได้สวยงามขึ้น

การติดตั้ง Pylint เนื่องจากยังไม่มี package ใน repository ของ ubuntu ให้เราติดตั้ง PIP ซึ่งเป็น package installation ของ Python ก่อนจากนั้นค่อยติดตั้ง Pylint ครับ

หมายเหตุ: การติดตั้งด้วย pip เป็นการดาวน์โหลดซอร์สโค้ดมาคอมไพล์แล้วติดตั้งหากเกิดปัญหาติดตั้งแล้ว error ให้ติดตั้ง package build-essential และ python3-dev  ไปด้วย

1.4 [Optional] ติดตั้ง Nosetest3

ช่วยในการรันเทสให้ง่ายขึ้น ไม่ต้องพิมพ์คำสั่งยาวๆ อย่าง python -m unittest [PATH.TO.MODULE]  อีกต่อไปแค่สั่ง nosetest3  มันจะวิ่งหาไฟล์ที่มีเทสเคสอยู่ให้

Nose สำหรับ Python3 มีอยู่ใน repository แล้วติดตั้งผ่าน APT ได้เลย

2. สร้างไดเรกทอรีของโปรเจกต์

ให้สร้างโครงสร้างไดเรกทอรีตามนี้ครับ

ที่จำเป็นคือ lib/  และ tests/  โครงสร้างไดเรกทอรีอย่างน้อยๆ ก็เป็นไปตามนี้ครับส่วนใครจะเพิ่ม bin/  doc/  data/  อะไรก็แล้วแต่ครับหน้าที่ของแต่ละไดเรกทอรีก็ตามแต่เราจะแยก เช่น

  • lib/  สำหรับคลาส/ฟังก์ชันของโปรแกรม
  • tests/  สำหรับเทสที่เราเขียนขึ้นมา
  • bin/  สำหรับไบนารีไฟล์หรือสคริปต์ที่ช่วยอำนวยความสะดวกต่างๆ
  • doc/  สำหรับ documents ของโปรแกรม
  • data/  สำหรับเก็บ data ภายนอกที่ใช้ในโปรแกรม เช่น ไฟล์ข้อมูลแบบ text, ไฟล์ฐานข้อมูล

สำหรับไดเรกทอรีที่เราจำเป็นต้อง import เข้ามาใช้งาน เช่น ไดเรกทอรี lib/ จำเป็นต้องมีไฟล์ __init__.py  ด้วยเสมอครับ เป็นสร้างเป็นไฟล์เปล่าก็ได้ (รายละเอียดอื่นๆ ลองไปหาอ่านดูเองนะ) ก็จะได้โครงสร้างของไดเรกทอรีประมาณนี้

3. เริ่มต้นด้วยการเขียนเทสก่อน

เสร็จแล้วก็เริ่มเขียนเทสก่อนเลยครับ

tests/awesome_test.py

*หมายเหตุ: ชื่อเมธอดต้องขึ้นต้นด้วย test_ เสมอ unittest จึงจะมองเห็น

เขียนโค้ดเสร็จอย่าลืมรัน Pylint ดูด้วยนะครับว่ามีตรงไหนเขียนไม่ถูกมาตรฐานหรือเปล่าโดยใช้คำสั่ง

เสร็จแล้วลองรันเทสดูครับ ซึ่งแน่นอนว่ามันจะ fail แดงเถือกเลย (แหงหละ) ให้เราแก้ตามที่ error มันฟ้อง การรันเทสใช้คำสั่ง

หรือถ้าต้องการเทสทั้งไดเรกทอรี tests/ ก็สั่งแบบนี้

หรือถ้าใช้ Nosetest ก็รันคำสั่งได้เลย

4. เขียนโค้ดเพื่อทำให้ error ที่เกิดขึ้นหายไป

lib/awesome.py

จากตัวอย่างนี้คลาส Awesome มีเมธอดเดียวคือ hello() ซึ่งจะ return string ‘Hello World’ ออกมา

5. รันเทสอีกครั้ง

จากข้อ 4 ผมตั้งใจวางบั๊กไว้ คือลบตัว ‘d’ ในคำว่า ‘World’ ออกไปเพื่อให้เทสไม่ผ่าน เมื่อเรารันเทสจะพบว่าเกิด error ขึ้นเพราะสิ่งที่เราคาดไว้ (expect) กับความจริงที่เกิดขึ้น (actual) มันไม่เท่ากันจะเกิด error ประมาณนี้ออกมาครับ

assert method ไม่ได้มีแค่ assertEqual นะครับยังมี method อื่นให้ใช้อีกเยอะเลย เข้าไปดู assert method ทั้งหมดใน document ของ module unittest ได้ครับ

6. ถ้ามีบั๊กอยู่ให้แก้ไขแล้วเทสใหม่จนกว่าจะผ่าน

ให้เราแก้ไข hello() ให้ถูกโดยการเพิ่ม ‘d’ เข้าไป แล้วรันเทสอีกครั้ง เทสควรผ่านและมีข้อความตามนี้ออกมา

เสร็จแล้วครับ สนุกเหมือนเล่นเกมเลยเนอะ

เวลาจะเอาโค้ดไปใช้งานเครื่องอื่นหรือคนอื่นเอาไปพัฒนาต่อก็รันเทสดูก่อนถ้า fail ก็แก้เป็นเคสๆ ไปจนผ่านครับ

Be First to Comment

Leave a Reply

Your email address will not be published.