วันพฤหัสบดีที่ 29 มกราคม พ.ศ. 2558

Test-Driven Development with Python - สรุป Chapter. 1-4

คำสำคัญที่พูดถึงในหนังสือ TDD with Python

Test-Driven Development (TDD) 

Test-Driven Development (TDD) คือรูปแบบการพัฒนา app โดยการเริ่มจากการสร้างตัว test ก่อน แล้วจึงเขียนโค้ดของโปรแกรม เปรียบได้กับการวาง spec ของโปรแกรมที่เราต้องการก่อนจึงค่อยสร้างโปรแกรมตาม spec ที่เราวางไว้ ช่วยให้สามารถสร้างโปรแกรมได้ตามความต้องการ การสร้างตัว test ย่อยๆของแต่ละส่วนสามารถทำให้รู้ตำแหน่งที่เกิด bug ได้โดยไม่ต้องไล่โค้ดทั้งหมด อีกทั้งยังสามารถลอง test โค้ดในขณะกำลังสร้างได้เรื่อยๆ

แหล่งที่มาเพิ่มเติม :
http://www.siamhtml.com/test-driven-development-introduction/

Functional Test vs Unit Test

Functional Test คือการทดสอบในมุมมองของ user เป็นการมอง app ที่เราทดสอบจากด้านนอกเปรียบเสมือน "black box" โดยไม่สนใจว่ามีการทำงานอย่างไรอยู่ภายใน สนใจเพียงว่า output ที่ได้นั้นมีการทำงานที่ถูกต้องตรงตามความต้องการหรือไม่

Unit Test คือการทดสอบในมุมมองของ programmer คือการมอง app โดยสนใจที่ตัวโค้ด เป็นการ test ที่ช่วยในแก้ bug ในการเขียนโค้ด ตรวจสอบว่าโค้ดที่เราเขียนนั้น error ตรงไหน อย่างไร ช่วยให้สามารถเขียนโค้ดได้ถูกต้อง ไม่มี bug

Refactoring

Refactoring คือ การกลับมาปรับปรุงโค้ดที่เคยเขียนไปแล้วและทำงานได้อย่างถูกต้องแล้ว ให้สั้นลง กระชับมากขึ้น ทำงานได้เร็วมากขึ้น อ่านได้ง่ายมากขึ้น ลดความซับซ้อนของโค้ด

แหล่งที่มาเพิ่มเติม :
http://meewebfree.com/site/php-codeigniter/432-what-is-refactoring-code
http://refactoring.com/

สรุปการทดลองใช้งานคำสั่งตามหนังสือ TDD with Python

Functional Test
  • การสร้าง Project ของ Django สำหรับเก็บ web app ของเรา
1. สร้างไฟล์ functional_tests.py สำหรับเป็นตัวทดสอบ functional test ซึ่งตัว Functional Test เราจะใช้ library ของ Selenium ในการเขียน
2. สร้างที่สำหรับเก็บโปรเจคด้วยคำสั่ง
django-admin.py startproject superlists
 จะสร้างโฟล์เดอร์ชื่อ superlists และมีไฟล์ย่อยๆดังนี้
.
├── functional_tests.py
└── superlists
    ├── manage.py
    └── superlists
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py
 3. ย้ายไฟล์ functional_tests.py เข้าไปอยู่ในโฟล์เดอร์ superlists อันนอกโดยคำสั่ง
mv functional_tests.py superlists/

  • การรัน Development Server ของ Django 
การทดสอบ functional test จะเป็นที่จะต้องสร้าง Server ขึ้นมาสำหรับรัน functional test โดยใช้คำสั่ง
python3 manage.py runserver
จะได้ Server บน IP 127.0.0.1 port 8000

  • การรัน Functional Test นั้นต้องรันบน Server ข้างต้นซึ่งรันโดยใช้คำสั่ง
python3 functional_tests.py

Git Repository
Git Repository คือ library หนึ่งที่สำหรับใช้จัดการ version control system (VCS) ซึ่งจะผูกมัดกับไฟล์และบันทึกแต่ละ version ของไฟล์งานเราไว้ หากเราเขียนโค้ดแล้วเกิดบัคเราก็สามารถเรียกเวอร์ชั่นก่อนหน้าเพื่อที่จะเรียกกลับมาดูหรือเขียนใหม่ต่อจากเวอร์ชั่นก่อนหน้าที่จะบัคได้

แหล่งที่มาเพิ่มเติม :
http://git-scm.com/book/th/v1
http://www.narisa.com/forums/index.php?app=blog&blogid=9&showentry=2814

การสร้าง Git Repository ใหม่เราจะเข้าไปในโฟลเดอร์ superlists แล้วใช้คำสั่ง
git init .
จะสร้าง Repository เปล่าๆขึ้นมาให้เรา


เราสามารถเพิ่มส่วนที่เหลือก็คือไฟล์งานต่างๆของที่อยู่ใน superlists ซึ่งเป็น current folder ของเราในตอนนี้โดยการใช้คำสั่ง
git add .
 โดยที่ "." หมายถึง current folder


การเรียกดูการเปลี่ยนแปลงของ Git Repository ใช้คำสั่ง
git status
ซึ่งจะแสดงว่าไฟล์ที่เราผูกมัด (commit) ไว้กับ git อันไหนมีการแก้ไขบ้าง ไฟล์ไหนที่เราพึ่งทำการผูกมัด ไฟล์ไหนที่ git ติดตาม (tracked)และไฟล์ไหนที่เราไม่ได้ให้ git ติดตาม (untracked)


การยืนยันไฟล์ที่มาจาก git add ให้เข้าไปใน Git Repository ใช้คำสั่ง
git commit
ซึ่งจะเก็บไฟล์ที่ add เข้ามาใหม่และบันทึกการแก้ไขไฟล์ไว้เป็นเวอร์ชั่นใหม่และมีการเปิดหน้า nano ให้เขียน comment ไว้เพื่อให้เรารู้ว่าตอนนี้โปรแกรมที่เราเขียนนั้นสำเร็จไปถึงไหนแล้ว เวลาเรียกกลับมาดูจะได้หาได้ง่าย


git diff
เป็นคำสั่งที่จะแสดงการแก้ไขไฟล์ที่เราผูกมัดไว้อย่างละเอียด ว่ามีการเพิ่มเข้าลบออกอย่างไร


git commit -a
-a คือจะบันทึกการเปลี่ยนแปลงทั้งหมดของไฟล์ที่เรา commit ไว้และเปลี่ยนไฟล์เป็นไฟล์ที่ git จะติดตาม (tracked) อัตโนมัติ

git commit -m "<comment>"
-m คือเราสารมารถเขียน comment ต่อท้ายคำสั่งได้เลยโดยไม่ต้องเข้าไปแก้ในหน้า nano

git log --oneline
จะแสดงเวอร์ชั่นและ comment ต่างๆที่เราได้บันทึกไว้



Unit Test
การเขียน Unit Test หากจะใช้ library ของ python นั่นสามารถเขียนในไฟล์ functional_tests.py ได้เลยโดย import unittest

หรือหาจะใช้ unit test ของ Django ก็ใช้ได้ง่าย เนื่องจาก Django จะสร้างไฟล์สำหรับทดสอบ unit test ให้อัตโนมันติเมื่อเราสร้าง app โดยมีขั้นตอนดังนี้

  • การเริ่มสร้าง app สำหรับ project ใช้คำสั่ง
python3 manage.py startapp lists
 ตามหนังสือนั้นเราจะสร้าง app สำหรับบันทึกรายการสิ่งที่เราจะต้องทำ จึงใช้ชื่อ app ว่า lists โดยที่แต่ละโปรเจคนั้นสามารถมีได้มากกว่า 1 app เมื่อสร้าง app แล้วจะมีการสร้างโฟล์เดอร์ชื่อ lists
superlists/
├── db.sqlite3
├── functional_tests.py
├── lists
│   ├── admin.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── superlists
    ├── __init__.py
    ├── __pycache__
    ├── settings.py
    ├── urls.py
    └── wsgi.py

  • การเขียน unit test นั้นตามที่บอกข้างต้นคือ Django จะสร้างไฟล์สำหรับการ test ให้เราอัตโนมัติเมื่อเราสร้าง app ซึ่งเป็นไฟล์ชื่อว่า lists/tests.py
from django.core.urlresolvers import resolve
from django.test import TestCase
from lists.views import home_page 

class HomePageTest(TestCase):

    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/')  
        self.assertEqual(found.func, home_page) 

จากตัวอย่างเป็นการฟังก์ชั่น resolve จะทำการแก้ไข '/' ให้เป็นมุมมองแบบ url ก็คือเป็น root ของหน้าเว็บ (home page) แล้วฟังก์ชั่น assertEqual จะตรวจสอบว่าหน้าที่ import มาจากไฟล์ lists.views นั้นเป็นหน้า home page จริงๆหรือไม่




from django.core.urlresolvers import resolve
from django.test import TestCase
from django.http import HttpRequest

from lists.views import home_page


class HomePageTest(TestCase):

    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/')
        self.assertEqual(found.func, home_page)


    def test_home_page_returns_correct_html(self):
        request = HttpRequest()  
        response = home_page(request)
        self.assertTrue(response.content.startswith(b'<html>'))
        self.assertIn(b'<title>To-Do lists</title>', response.content)
        self.assertTrue(response.content.endswith(b'</html>'))

จากตัวอย่างมีการ test ว่าในโค้ด html ของตัว home page นั้นต้องขึ้นต้นด้วย <html> มี <title>To-Do lists</title> อยู่ในช่วงกลางๆของโค้ด html และปิดท้ายโค้ดด้วย </html>
และตามหนังสือให้แก้ในไฟล์ lists/views.py ซึ่งเป็นไฟล์ที่ lists.tests.py จะเรียกใช้ฟังก์ชัน home_page โดยในที่นี้จะให้ลอง return เป็นโค้ด html ตามที่ test.py ต้องการ
from django.http import HttpResponse
    def home_page(request):
        return HttpResponse('<html><title>To-Do lists</title></html>')


Refactoring to Use a Template

Django สามารถสร้าง template สำหรับหน้าเว็บได้ โดยจะต้องสร้าง folder ที่ lists/templates ตามข้อตกลงของ Django และลองสร้าง file lists/templates/home.html เพื่อลองเขียนเป็นโค้ด html จริงๆ (ตอนแรกเราให้ตัว view.py return โค้ด html ไปเลยเพื่อทดสอบตัว test ก่อน เป็นการ Refactoring โปรแกรมให้มีการทำงานสมจริงยิ่งขึ้น) โดยไปแก้ให้ view.py มาอ่านโค้ด html จากไฟล์ home.html จริงๆดังนี้
from django.shortcuts import render

def home_page(request):
    return render(request, 'home.html')

แต่เนื่องจาก lists เป็น app ที่เราสร้างขึ้นมา แต่ทาง Django ไม่ได้หาให้เราอัตโนมัติเราจำเป็นจะต้องบอก Django เพิ่มเติมในไฟล์ superlists/settings.py ว่าเรามี app ชื่อ lists โดยเพิ่มคำว่า 'lists', ในฟังก์ชั่นดังนี้
# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'lists',
)
จากนั้นก็จะเป็นการเพิ่มส่วนของโค้ด html ให้มี spec ตามที่ตัว Functional test ต้องการ

อ้างอิงจากหนังสือ Test-Driven Development with Python by Harry Percival :
http://chimera.labs.oreilly.com/books/1234000000754

ไม่มีความคิดเห็น:

แสดงความคิดเห็น