in Django, Python

Test Driven Development ด้วย Django Unit Testing (ตอนที่ 1)

Django มีสุดยอดแห่ง unit testing ที่ build in มาในตัว framework เรียบร้อยแล้ว, คนมักจะไม่ค่อยใส่ใจกับมันเท่าไรนักเพราะว่ามันมาแบบ “out of the box” แต่นั่นไม่ได้หมายความว่าเราจะละเลยส่วนั้นได้ เมื่อเราสร้าง Application ของเราเองขึ้นมา เพราะว่าเทสต์นั้นทำให้เรา “คิดออก” ว่าอะไรที่ควรจะทำให้เสร็จ เพราะฉะนั้นเราจะมาพูดเรื่องทำอย่างไรให้ Application สามรถทำงานได้เป็นอย่างดี
เมื่อมาถึงตรงนี้แล้วผมจะพยายามแสดงให้เห็นว่า ทำยังไงจะใช้ TDD ใน Django Application เราจะสร้าง Project เล็กๆ และมี Application เพียงตัวเดียว และใช้ Unit testing เป็นตัวกำหนด ว่าจะออกแบบ algorithm และจะ implement class อย่างไร

ทำไมต้องเทสต์ตลอด?
หลายๆ คนพูดถึงเรื่อง Test Driven Development แล้วยังมีโปรแกรมเมอร์ที่มีประสบการณ์หลายๆ คน ที่ไม่ต้องการจะทำ TDD เพราะว่าคนเหล่านั้น ไม่ยอมเปิดรับการเปลี่ยนแปลงใหม่ๆและไม่ยอมที่จะเรียนรู้  คนกลุ่มนี้สร้างระบบและจัดการในเรื่องการ maintain code เขาอาจจะเขียน code เป็นเดือน หรือแม้แต่เป็นปี code ที่เขียนนั้นได้รับการทดสอบเต็มรูปแบบจึงมั่นใจในความแข็งแกร่ง ทำไมยังจะต้องเขียน unit test?
โปรแกรมเมอร์รุ่นใหม่ไม่รู้ว่าจะเริ่มต้นกับเทสต์อย่างไร พวกเขาเขียน code แล้วจึงเขียนเทสต์ แล้วจึงพบกับความท้อแท้ เพราะว่าต้องเขียน code วนซ้ำไปซ้ำมาถึงสองครั้ง เพื่อที่จะพบกับผลลัพท์ที่แทบจะเอาตัวไม่รอด
เรื่องทั้งคู่เป็นเรื่องน่าเศร้าที่เราจะพบเห็นได้บ่อยๆ เพราะมีบางสิ่งที่ TDD ได้มอบให้โปรแกรมเมอร์และองค์กรนั้นคือ :

  • การรับประกันคุณภาพที่มีประสิทธิภาพมากขึ้น เพราะว่า QA Engineer และ Tester ไม่ต้องมานั่งตรวจสอบฟังชั่นการทำงาน, แล้วมันให้ code ที่ดีกว่าเหรอ? หมายความว่ายังไงกันแน่? หมายความว่าต่างคนก็ต่างทำงานใขขอบเขตของตน นั่นสร้างประโยชน์โดยรวมแก่บริษัททั้งหมด ถ้าโค้ดพื้นฐานและฟังชั่นทั้งหมด สามารถเทสต์ได้โดยอัตโนมัต QA ก็สามารถที่จะใช้เวลาเพิ่มขึ้นในการค้นหาบักที่ไม่ชัดเจน และแก้ไขมันเสีย ก่อนที่มันจะทำลายโปรแกรมทั้งหมด.
  • การประสานงานในองค์กรดีขึ้น TDD สร้างความไว้เนื้อเชื่อใจในองค์กรทั้งหมด โปรแกรมเมอร์ต้องการความเชื่อใจในความสามารถและทักษะ เมื่อทุกคนในองค์กรใช้ TDD นั่นหมายความว่าโค้ดที่มากจากคนอื่นๆในองค์กรจะต้องทำงานได้ถูกต้อง นั่นไม่ได้เพียงแค่สร้างความเชื่อใจระหว่างโปรแกรมเมอร์ แต่ยังรวมถึงส่วนอื่นๆในองค์กร เช่นฝ่ายการตลาด Business resource รวมไปถึง QA
  • ความเป็นมืออาชีพ คำว่าโปร ไม่ได้หมายความเพียงแค่เขียนโค้ดนับพันบรรทัดในวันเดียว แต่หมายถึงการเขียนโค้ดที่มีประสิทธิภาพ ง่ายต่อการบำรุงรักษา ง่ายต่อการ config หมายถึงโค้ดที่ “ทำงานได้” ตลอดเวลา ไม่ใช้แค่ทำงานได้ แต่ต้องผ่านการเทสต์และมีเอกสารที่ดีด้วย
  • สร้างแรงกดดันเพื่อน “ทำสิ่งที่ถูกต้อง” เมื่อสมาชิกในทีมรวมไปถึงหัวหน้าทีมใช้งาน TDD ดูแลโค้ดของตัวเองให้มีประสิทธิภาพ มันจะสร้างแรงกระตุ้นต่อองค์กรโดยรวม ให้เกิดสภาพแวดล้อมที่ดีต่อการฝึกฝน

Test Driven Development ภายไต้ unit test จะสร้างซอฟต์แวร์ที่มีประสิทธิภาพ และสามารถทำงานได้อย่างดี บทความอันนี้จะอธิบายถึงวิธีการพัฒนาโปรแกรมที่ซับซ้อนอย่าง Bank Routing Number (ABN) validation class
Create the Project and Application
อย่างแรกที่จะทำคือสร้าง Application ชื่อ
valid_lib
โดยกำหนดให้เป็น Service platform ที่อนุญาติให้ข้อมูลทางการเงินอันสลับซับซ้อนสามารถ validate ผ่าน ReSTful interface. ในตอนนี้เรายังไม่ต้องกังวลเกี่ยวกับ Service หรือ Business model เพียงแค่คิดเรื่องเกี่ยวกับการคำรวนและ validation algorithm ก็พอ ตอนนี้เริ่มจากสร้างแอปก่อน
./manage.py startapp finvs
และเพิ่ม finvs ลงไปบน INSTALL_APP ในไฟล์ settings.py สร้างโฟลเดอร์ชื่อ util ลงในโฟลเดอร์ finvs อีกทีนึงอย่างในรูป

ลองดูที่ไฟล์ test.py ในโฟลเดอร์ชื่อ finvs เราจะเริ่มจากตรงนั้น Django ได้สร้าง unit test ไฟล์ไว้ให้เราแล้ว
ABN Validation
ABA (American Banker’s Association) routing numbers (ABNs) ใช้สำหรับระบุสถาบันการเงินตอนที่สร้าง transaction ตัวเลขนี้จำเป็นต้องใช้เวลาที่จะโอนเงินผ่านธนาคารโดยตรง บริการจ่ายเงินอัตโนมัต หรือเมื่อเราต้องการจะจ่ายเงินเผ่านโทรศัพท์ ระบบออนไลน์เป็นต้น
เลขเหล่านี้มีความหมายต่างๆดันไป และยังรวมถึง checksum digit เพื่อที่จะสามารถตรวจสอบได้ว่าเลขที่ให้มานั้นถูกต้องหรือไม่
Algorithm จะตรวจสอบดังนี้ 3(d1+d4+d7)+7(d2+d5+d8)+(d3+d6+d9) mod 10 = 0 แบบนี้

import unittest
#from django.test import TestCase
#Here are some bank numbers that we can run tests against...
SHORT_BAD_ABN_NUM = 1000012
LONG_BAD_ABN_NUM = 255073345999
BAD_ABN_NUM = 255073545
GOOD_ABN_NUM = 255073345
class ValidatorsTestCase(unittest.TestCase):
    def setUp(self):
        self.value = GOOD_ABN_NUM
    def testAbnAlgorythm(self):
        """Tests the algorythm with a known good bank number.
           This test is here to create the algorythm to be used
             in the validator implementation.  When it's right,
             cut and paste it into the validator class.
           The algorithm checks the first second and third number
            and adds them together, then iterates through every
            three numbers (total of 9) to return a value.
           If the resulting sum is an even multiple of ten
            (but not zero), the ABA routing number is good.
           """
        n=0
        bank_str = str(GOOD_ABN_NUM)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j])
                ti = int(bank_str[j + 1])
                tii = int(bank_str[j + 2])
                n += (t * 3)+ (ti * 7)+ tii
            self.assertTrue((n != 0) & ((n % 10) == 0))

หลังจากนั้นก็รันเทสต์

python manage.py test finvs.ValidatorsTestCase

น่าจะได้ผลลัพท์อย่างนี้

$ python manage.py test finvs.ValidatorsTestCase
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Installing index for auth.Permission model
Installing index for auth.Message model
True
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Destroying test database...

ระหว่างที่กำลังรันเทสต์นั้น Django จะสร้างและลบ Test Database ให้โดยอัตโนมัต แจ่มใหม? แต่จริงๆแล้วเราจะยังไม่ได้ใช้โค้ดชุดนี้ เราจะสร้าง Production code ต่างหาก แต่เมื่อดูตัวเทสต์ จะพบว่านี่คือพื้นฐานของการ implement ระบบจริงๆ
ครั้งหน้าเราจะมาต่อที่การ implement จริงๆกัน

Write a Comment

Comment