วันศุกร์ที่ 15 พฤษภาคม พ.ศ. 2558

วันเสาร์ที่ 7 กุมภาพันธ์ พ.ศ. 2558

TDD - 4 What Are We Doing with All These Tests?

Chapter 04 - What Are We Doing with All These Tests?  

ในการ test ครั้งนี้จะเป็นการ test ในส่วนของ การ รันค่าจากไฟล์ html ที่ตั้งให้เป็น
Home page ซึ่ง Home page ในที่นี้จะมีโค้ด HTML ที่จะ render ให้ client ผู้ซึ่ง request เข้ามา
โดย functional_tests.py นี้นั้นโดยที่รู้ ๆ กันอยู่ว่า เป็นในส่วนของกา


 รตรวจสอบการแสดงผลในมุมมองของผู้ใช้ว่ามี object นี้ไหม มีการแสดงผลที่ถูกต้องหรือเปล่า เมื่อให้ input เป็นในรูปแบบนี้แล้ว

output ตรงตาม concept ที่ตั้งใจไว้หรือเปล่า โดยจะมีการเรียกใช้ function ในการตรวจสอบ
อย่างเช่น self.assertIn('To-Do', self.browser.title)
เป็นการตรวจสอบค่า ที่อยู่ใน title ของ browser ของเราว่ามีคำว่า 'To-Do' หรือเปล่าหากไม่มีก็จะขึ้น error และปิดโปรแกรมลง ตามฟังก์ชัน teardown(self){...} (เป็นฟังก์ชันที่จะเรียกใช้เมื่อมีการ error)
ของ platform หรือมีการ return ค่าออกมาเป็น เท็จ (ไม่เห็น object)
ในตอนแรกนั้น functional_tests.py จะทำการเรียก server ที่รันไว้ ที่ localhost:8000 จากในบรรทัด
 self.browser.get('http://localhost:8000') โดย urls.py ก็จะทำหน้าที่ส่งต่อให้ function home_page ในไฟล์ lists/views.py โดยไฟล์ views.py นั้นก็จะทำหน้าที่โดยการแสดง home page ออกมา  ซึ่งโค้ดนั้นจะถูกตรวจสอบโดย test.py ว่าถูกต้องตามหลักไวยากรณ์ไหม มีแทกอะไรที่ขาดไปบ้างจาก platform ที่เราต้องการ 
เช่น ใช้ฟังก์ชั่น self.assertIn(b'<title>To-Do lists</title>', response.content)
หมายความว่าให้เช็คว่าในข้อความที่ได้ response ออกมานั้นมีคำว่า '<title>To-Do lists</title>' หรือไม่
หลังจากที่เราสร้างไฟล์เรียบร้อยแล้วก็ให้ใส่ code ลงไปในไฟล์โดยเริ่มต้นให้ลองใส่
===============views.py============== 
from django.shortcuts import render

def home_page(request):
    return render(request, 'home.html') 
เมื่อได้ทดสอบเขียนโค้ดดูแล้วลองรันโดย $ python3 manage.py test

 
ซึ่งในตอนแรกนั้นยังไม่ได้ทำการสร้างไฟล์ home page ก็มี error แจ้งเตือนขึ้นมาว่าไม่พบไฟล์ดังกล่าว ต่อมาจึงสร้างไฟล์ home.html ขึ้นใน directory -> lists/templates/ และทำการเขียนโค้ดลงในไฟล์ และเพิ่มโค้ดลงในส่วนของ settings.py file
===============home.html============== 
<html>
    <title>To-Do lists</title>
</html> 
 
==============Settings.py=============
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'lists',
) 
หลังจากนั้นทดสอบรันโค้ดอีกรอบ $ python3 manage.py test
error ที่แจ้งเตือนว่าไม่พบไฟล์นั้น ได้หายไปแล้วกลายเป็นแจ้งเตือน error เกี่ยวกับ code ที่ return ค่า
กลับมาแทน หลังจากนั้นทดสอบเปลี่ยน code ที่ test.py โดยให้ที่การแบ่งค่าออกมาเป็น บรรทัดแทน

============================tests.py========================== 
self.assertTrue(response.content.strip().endswith(b'</html>')) 
ทดสอบดูอีกที $ python3 manage.py test ได้ผลลัพธ์ออกมาว่า การตรวจเช็ตไม่มีข้อผิดพลาดของ syntax
สามารถเปลี่ยนรูปแบบโค้ดเป็นการ decode ได้โดยเปลี่ยนรูปแบบ code ของ tests.py

============================tests.py========================== 
from django.template.loader import render_to_string
[...]

    def test_home_page_returns_correct_html(self):
        request = HttpRequest()
        response = home_page(request)
        expected_html = render_to_string('home.html')
        self.assertEqual(response.content.decode(), expected_html)
โดยผลที่ได้ก็จะเหมือนเดิม
ทดสอบการเปลี่ยนโค้ดของ home.html
===============================home.html=========================== 
<html>
    <head>
        <title>To-Do lists</title>
    </head>
    <body>
        <h1>Your To-Do list</h1>
        <input id="id_new_item" placeholder="Enter a to-do item" /> 
    </body>
</html>
โดยโค้ดในส่วนของ input นั้นจะเป็น input ในรูปแบบ default คือ textbox โดยที่ functional_tests.py
จะมีโค้ด inputbox = self.browser.find_element_by_id('id_new_item') ที่จะนำเอาค่า ของ object ใน
id : id_new_item เก็บไว้ในตัวแปรที่ชื่อ input เมื่อโปรแกรมเรียกใช้คำสั่ง
self.assertEqual(inputbox.get_attribute('placeholder'),'Enter a to-do item') ก็จะมีการเช็คค่า ว่าค่า placeholder ใน object นั้นตรงกับ 'Enter a to-do item' หรือเปล่า หากไม่ตรงก็จะ error และไปที่
function teardown โดยอัตโนมัติ ลองเพิ่ม หลังจากนั้นก็ทำการ git commit ค่าที่ปลี่ยนแปลงไป

วันอาทิตย์ที่ 1 กุมภาพันธ์ พ.ศ. 2558

TDD - 03 Testing a Simple Home Page with Unit Tests

Chapter 3 - Testing a Simple Home Page with Unit Tests 

เมื่อคราวที่แล้วได้ทดสอบการ ใช้ git ในการสร้างไฟล์ขึ้นมาและเรียนรัน ไฟล์ที่สร้างขึ้น
และมีการ set git ใน directory ที่เราได้สร้างขึ้นมา
ในครั้งนี้จะเป็นการสร้าง Home page อย่างง่าย ๆ และการ ทดสอบการรัน 
file Homepage ที่สร้างขึ้น และแก้บัคกับสิ่งที่เกิดขึ้น
เนื่องจากว่า ผมได้ทดสอบ run ไปแล้ว 1 รอบ เมื่อกลับไปลอง run ใหม่แล้วค่าที่ 
error มันไม่หมือนเดิมจึงไม่สามารถที่จะ run output ออกมาให้ได้ทั้งหมดครับ
เริ่มต้น สร้าง List ในโฟลเดอร์ที่เราเริ่มต้นโปรเจคขึ้น ในที่นี้
ให้เราสร้าง app list โดยพิมพ์โค้ดลงไปดังนี้





  •   python3 manage.py startapp 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 
    
    
    โดยหลังจากที่ได้ run code แล้วก็จะมี directory หน้าตาแบบนี้ขึ้นที่ directory ที่เราสร้างโปรเจคขึ้นมา
    โดยหลังจากนั้นก็ได้ทำการเปลี่ยนแปลงข้อมูลในไฟล์ test.py ที่อยู่ใน /list/
    ดังนี้
    from django.test import TestCase
    
    class SmokeTest(TestCase):
    
        def test_bad_maths(self):
            self.assertEqual(1 + 1, 3)
    แล้วหลังจากนั้น ทดลอง run test โดยใส่โค้ดดังนี้

    python3 manage.py test
    
    หลังจากนั้น หากผิดพลาดเมื่อได้ค่า error มาเราก็จะนำข้อผิดพลาดที่เกิดขึ้นมา
    แก้ไข โดยจากตัวอย่างไม่ได้ผิดพลาดแต่อย่างใดแต่ได้ผลลัพธ์ที่ว่า 2 != 3
    test.py เป็นไฟล์ที่เราเขียนขึ้นมาเพื่อใช้ทดสอบข้อผิดพลาดต่าง ๆ ภายใน TDD 
    ซึ่งผลลัพธ์สามารถที่จะทดสอบได้โดยทดลอง run ได้ใน python
    ส่วน view.py เป็นในส่วนของมุมมองที่จะแสดงผลให้ client ดู 
    ทดสอบการรันในส่วนของ view.py แล้ว จะได้ error เกี่ยวกับในส่วนของ
    function การแสดงผลออกมา ว่ามีการ error ในส่วนใดและจะสามารถนำในส่วนที่
    มันฟ้องว่า error ในส่วนใด กลับมาแก้ไขได้
    ในส่วนของ urls.py จะเป็นส่วนของการ map client ไปสู่ url ต่างๆที่ client 
    เรียกขอมาซึ่งจะมี error ขึ้นมาในส่วนของ url ที่ผิดพลาดเช่น
    ภายใน urls มีโค้ดว่า
    urlpatterns = patterns('',
        # Examples:
        url(r'^$', 'superlists.views.home', name='home'),
        # url(r'^blog/', include('blog.urls')),
    
        # url(r'^admin/', include(admin.site.urls)),
    )
    เมื่อ run python manage.py test แล้วได้ output ออกมาดังนี้

    django.core.exceptions.ViewDoesNotExist: Could not import
    superlists.views.home. Parent module superlists.views does not exist.
     
    มันฟ้องว่าไม่พบ file ที่เราค้นหาอยู่ เนื่องจากว่าใส่โค้ดที่จะค้นหาในส่วนของไฟล์ url ผิดพลาด
    จึงไม่สามารถที่จะค้นหาพบได้ 
    
    
    ค่าที่ error ออกมานั้นมักจะมี วิธีแก้ไขออกมาให้ด้วยเสมอ ซึ่งวิธีแก้ไขนี้เอง
    ที่เราสามารถที่จะนำไปปรับแก้โค้ดของเราทีละนิด ซึ่งทุกครั้งที่แก้ไข หากโปรแกรมนั้นยัง
    error อยู่ก็มักที่จะมี solution หรือแนวทางในการแก้ไขปัญหาต่อ ๆ ไปที่จะให้เราทำไปตาม
    ขั้นตอน จบกว่าที่มันจะไม่ error และสามารถที่จะ run ได้ตามปกติ

    วันเสาร์ที่ 24 มกราคม พ.ศ. 2558

    Test 03 - csv data sort in cgi


    Test 03 csv data sort in cgi

    ในคราวนี้ผมจะทดสอบโดยการ ให้ cgi เปิดไฟล์ csv ออกมาแล้วสามารถที่จะจัดเรียง
    ข้อมูลในนั้นและแสดงออกมาได้
    ขั้นแรกเลย จัดเตรียม server ให้พร้อมที่จะ run cgi
     ต่อมาเข้าไปที่ 0.0.0.0:8000/#dir/#file โดยในคราวนี้ผมได้ตั้งชื่อไฟล์ว่า datasort.cgi
     หน้าตาของโปรแกรมเป็นแบบนี้ ในขั้นแรกจะให้เลือกไฟล์ csv ที่อยู่ใน directory ที่จัดเตรียมไว้
    เพื่อที่จะนำมาใช้แสดงผล ในที่นี้เป็น /home/#user/bss/csv-data/ โดย user คือชื่อ username
    ให้เรากดเลือกไฟล์ใดไฟล์หนึ่งเพื่อนที่จะนำมาแสดงผล
     โปรแกรมก็จะแสดงผลข้อมูลในไฟล์ออกมา โดยในขั้นแรกจะยังไม่มีการจัดเรียงอะไร สามารถที่จะเปลี่ยนไฟล์ได้โดยไปที่ change file แล้วเลือกไฟล์ที่ต้องการ
     เมื่อเลือกไฟล์ที่ต้องการได้แล้วสามารถที่จะสั่งให้มันจัดเรียง ตาม id มาก - น้อย หรือตรงกันข้ามได้โดย
    ไปที่ Sort by
    ผลของการจัดเรียงเป็นดังนี้


    code ::


    #!/usr/bin/python
    #--coding: utf-8 -*-
    print "Content-type: text/html\n ; charset=utf-8"
    import os, cgi
    query = cgi.FieldStorage()
    selectedfile, command = "", ""
    for i in query.keys():
        exec(i + " = " + query[i].value)

    def sortby(obj, type):
        data = {}
        for table in range(len(obj)-1):
            if(obj[table][1] != ""):
                data[len(data)] = int(obj[table][1].replace("-", "").replace(" ", ""))
            else: data[len(data)] = 0   
        i = j = 0
        while i < len(data):
            j = i
            while j < len(data):
                if type == "up":
                    if data[j] < data[i]:   
                        dd = data[i]
                        data[i] = data[j]
                        data[j] = dd
                        dummy = obj[i]
                        obj[i] = obj[j]
                        obj[j] = dummy
                elif type == "down":
                    if data[j] > data[i]:   
                        dd = data[i]
                        data[i] = data[j]
                        data[j] = dd
                        dummy = obj[i]
                        obj[i] = obj[j]
                        obj[j] = dummy
                j += 1
            i += 1
        return obj
           

    print """
    <script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
    <script>
    function jumptofile(file){
        window.location.href = location.protocol + '//' + location.host + location.pathname+"?selectedfile=\\\""+file+"\\\"&command=\\\"\\\"";
    }

    function jumptoparam(command){
       
        window.location.href = location.protocol + '//' + location.host + location.pathname+"?selectedfile=\\\""""+selectedfile+"""\\\"&command=\\\""+command+"\\\"";
    }

    function check(part){
        $(part).toggle();
    }
    </script>
    """

    library = "/home/galible/bss/csv-data"
    trav = os.walk(library);
    datarows, datatables, title = {}, {}, {}
    if selectedfile == "":
        print "&nbsp;&nbsp;&nbsp;<h1><font color=\"red\">choose the file</font></h1>"
        for fol in trav:
            for file in fol[2]:
                print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font size=\"5\">>>> </font><a onclick=\"javascript:jumptofile(name)\" name=\""+str(fol[0])+"/"+str(file)+"\"><font size=\"5\" color=\"#FFAA55\">"+str(fol[0])+"/"+str(file)+"</font><font size=\"5\"> <<<</font></a><br>";
    else:
        print "<h1><font color=\"#0055FF\">detail for csv file : </font><font color=\"#FF5500\">"+selectedfile+"</font></h1>"   
        file = open(selectedfile, 'r')
        datatables = file.readlines()
        for row in datatables:
            if title == {}:
                title = row.split(",")
            else:
                datarows[len(datarows)] = row.split(",")
           
        print "change file : <select onchange=\"jumptofile(value)\">"
        for fol in trav:
            for file in fol[2]:
                print "<option value=\""+str(fol[0])+"/"+str(file)+"\" "+((str(file) == str(selectedfile.split("/")[-1])) and "selected" or "")+">"+str(file)+"</option>"
        print "</select><br>"
        if command == "":
            print "Sort by : <select onchange=\"jumptoparam(value)\"><option value=\"\" selected>no sort</option><option value=\"sortidup\">sort id up to down</option><option value=\"sortiddown\">sort id down to up</option></select><br>"
        if command == "sortidup":
            print "Sort by : <select onchange=\"jumptoparam(value)\"><option value=\"\">no sort</option><option value=\"sortidup\" selected>sort id up to down</option><option value=\"sortiddown\">sort id down to up</option></select><br>"
            datarows = sortby(datarows, "up")
        if command == "sortiddown":
            print "Sort by : <select onchange=\"jumptoparam(value)\"><option value=\"\">no sort</option><option value=\"sortidup\">sort id up to down</option><option value=\"sortiddown\" selected>sort id down to up</option></select><br>"
            datarows = sortby(datarows, "down")
       
        print "<table border=\"4\">"
        print "<tr>"
        for header in title:
            print "<td>" + header + "</td>"
        print "</tr>"
        for table in range(len(datarows)):
            print "<tr>"
            for row in datarows[table]:
                print "<td>" + row + "</td>"
            print "</tr>"
        print "</table>"

    Test 02 - directory list in cgi


    test 02 directory list by cgi

    จากคราวที่แล้วที่ได้ทดสอบ ให้ python เขียน html ดู คราวนี้จึงได้ทดสอบการใช้
    cgi ในการเขียนหน้าเว็บ โดย python
    ขั้นแรก จัดเตรียม เซิฟเวอร์ให้พร้อมที่จะรัน cgi

    ทดสอบรันโค้ดที่ได้เขียนมา
     โดยเข้าไปที่ 0.0.0.0:8000/#dir/#file โดย #dir คือ directory ที่อยู่ภายใน server และ #file คือชื่อของ file






    --code--

    #!/usr/bin/python
    print "Content-type: text/html\n\n"
    import cgi, cgitb, os

    datain = cgi.FieldStorage()
    directory, srcq = "", ""

    def spl(text):
        text = text.replace("/", "");
        text = text.replace(".", "");
        text = text.replace("~", "__sp__");
        return text

    def chknxtnode(text1, text2):
        root = ""
        leaf = ""
        if len(text1) > len(text2):
            root = text2
            leaf = text1
        else:   
            root = text1
            leaf = text2
        i, result = 0, 1
        while i < len(root):
            if root[i] != leaf[i]:
                result = 0
            i += 1
        return result

    def space(num):
        for i in range(num):
            print "&nbsp;"

    def namefol(part):
        part = part.split("/")
        return part[len(part)-1]
       

    def nbfol(part):
        print " <input type=\"button\" value=\"show\" name=\""+spl(part)+"\" id=\""+spl(part)+"=show\" onclick=\"javascript:showtools(name)\"> <"+spl(part)+" style=\"display:none\"><input type=\"button\" name=\""+part+"\" value=\"rename\" onclick=\"javascript:dirrename(name)\"><input type=\"button\" name=\""+part+"\" value=\"new file\" onclick=\"javascript:dirnewfile(name)\"></"+spl(part)+">"

    def nbfile(part):
        print " <input type=\"button\" value=\"show\" name=\""+spl(part)+"\" id=\""+spl(part)+"=show\" onclick=\"javascript:showtools(name)\"> <"+spl(part)+" style=\"display:none\"><input type=\"button\" name=\""+part+"\" value=\"delete\" onclick=\"javascript:filedelete(name)\"><input type=\"button\" name=\""+part+"\" value=\"rename\" onclick=\"javascript:filerename(name)\"></"+spl(part)+">"

    for i in datain.keys():
        exec(i + " = " + datain[i].value)

    print """
    <script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
    <script>
    function jumpto(){
        var query = document.getElementById("txtb1").value;
        var srcq = document.getElementById("txtb2").value;
        var old = \"""" + directory + """\"
        if(query == "-"){
            window.location.href = "dirls.py?directory=\\\""+old+"\\\"&srcq=\\\""+srcq+"\\\"";
        }else{
            window.location.href = "dirls.py?directory=\\\""+query+"\\\"&srcq=\\\""+srcq+"\\\"";
        }
    }

    function showtools(part){
        var enp = $(part);
        var data = document.getElementById(part+"=show");
        if(data.value == "show"){
            data.value = "hide";
            enp.show();
        }else{
            data.value = "show";
            enp.hide();
        }
    }

    function check(part){
        $(part).toggle();
    }

    function dirrename(part){
        while(true){
            datas = part.split("/")
            var folname = datas[datas.length - 1];
            var dirname = "";
            for(var i = 0; i < datas.length - 1; i++){
                dirname += datas[i];
            }
            var newname = prompt("Rename this folder to", folname);
            if(newname == null){
                break;
            }
            if($(dirname+newname).length == 0){
                xmlhttp = new XMLHttpRequest();
                xmlhttp.open("GET", "command.py?type=\\\"rename\\\"&dir=\\\""+part[0]+"\\\"&newname=\\\""+newname+"\\\"");
                xmlhttp.send();
                break;
            }else{
                alert("sorry guys but this name is exists in directory");
            }
        }
    }

    function dirnewfile(part){

    }

    function filedelete(part){

    }

    function filerename(part){

    }
    </script>
    """

    print "<html><head></head><body><b>Directory : <input type=\"textbox\" name=\"txtbdir\" id=\"txtb1\"> \"-\" for still use old directory<br>Search for : </b><input type=\"textbox\" name=\"txtsrc\" id=\"txtb2\"> <input type=\"button\" value=\"accept\" name=\"butselect\" onclick=\"javascript:jumpto()\"><br><br>"

    try:   
        if (directory != "") and (srcq == ""):
            print "<h1>Search in " + directory + "</h1>"
            try:
                data = os.walk(directory);
                updir = {}
                updir[0] = str(directory)
                for part in data:
                    while 1:
                        if chknxtnode(updir[len(updir) - 1], part[0]):
                            updir[len(updir)] = part[0]
                            break
                        else:
                            dummy = {}
                            for i in range(len(updir) - 1):
                                dummy[i] = updir[i]
                            updir = dummy
                    space(3*(len(updir)-2))
                    print "<b><a href=\""+part[0]+"\">"+part[0]+"</a></b> "
                    nbfol(part[0])
                    print "<br>"
                    for file in part[2]:
                        space(3*(len(updir)-1))
                        print "<a href=\""+part[0]+file+"\">"+file+"</a> size : "+ str(os.stat(str(part[0])+"/"+str(file)).st_size) + " b "
                        nbfile(str(part[0])+str(file))
                        print "<br>"
            except:    print "directory not exists !!";
        elif directory != "":
            print "<h1>Search for " + srcq + " in " + directory + "</h1>"
            try:
                data = os.walk(directory);
                for part in data:
                    if srcq in namefol(part[0]):
                        print part[0]
                        nbfol(part[0])
                        print "<br>"
                    for file in part[2]:
                        if srcq in file:
                            space(5);
                            print part[0] + " --> <a href=\""+str(part[0])+"/"+str(file)+"\">"+file+"</a>"
                            nbfile(str(part[0])+"/"+str(file))
                            print "<br>"
            except:    print "directory not exists !!";
        else: gotoexcept
    except:    print "<h2>Please enter some directory and push select...</h2>"

    ผลการทดสอบของ code

    ใส่ directory ที่ต้องการค้นหาลงไป
    หลังจากกด accept แล้วจะแสดงผลการค้นหาขึ้นมาทดสอบค้นหาคำ โดยการพิมพ์ key word ที่ต้องการค้นหาลงไปในช่อง search for หลังจากนั้นกด accept จะปรากฎ file ที่มีชื่อตรงกับคำที่ใส่ลงไป