วันพฤหัสบดีที่ 5 มิถุนายน พ.ศ. 2557

Chapter 3. Testing a Simple Home Page with Unit Tests

Chapter 3. Testing a Simple Home Page with Unit Tests

     เป็นการ test  home page แบบง่ายๆ
    อันดับแรกของการใช้งาน Django Appเริ่มต้นด้วยการสร้าง app lists ขึ้นมา และ ls ดูว่ามีโฟล์เดอร์ list ปรากฏขึ้นมาในโฟล์เดอร์ superlists

$python manage.py startapp lists
$ ls
db.sqlite3           functional_tests.py~  manage.py   Untitled Document
functional_tests.py  lists                 superlists

 
    unit tests และวิธีการทดสอบโปรแกรม ทำการแก้ไขไฟล์ ใน lists/tests.py และทำการรัน ไฟล์ tests.py

from django.test import TestCase

class SmokeTest(TestCase):

    def test_bad_maths(self):
        self.assertEqual(1 + 1, 3)

$ python manage.py test
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_bad_maths (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 6, in test_bad_maths
    self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)
Destroying test database for alias 'default'...

     เมื่อรันแล้วจะพบว่ามีการ Error เนื่องจากในฟังก์ชั่น test_bad_math มีการนำค่า 2 ค่ามาเปรียบเทียบกันแต่ในฟังก์ชั่นนำค่า 1+1 มาเทียบกับ 3 ซึ่งไม่เท่ากันจึง Error

    คำสัง git status เพื่อดูสถานะของแต่ละไฟล์
$ git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#    Untitled Document
#    functional_tests.py~
#    lists/
nothing added to commit but untracked files present (use "git add" to track)

     คำสั่ง git add lists เพื่อ add ไฟล์ lists ให้ git จัดการกับไฟล์นี้ให้
     คำสั่ง git diff --staged เพื่อดูการแก้ไขในแต่ละไฟล์
$ git add lists
$ git diff --staged
diff --git a/lists/__init__.py b/lists/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lists/admin.py b/lists/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/lists/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/lists/models.py b/lists/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/lists/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/lists/tests.py b/lists/tests.py
new file mode 100644
:

    คำสั่ง git commit -m "ตามด้วยชื่อที่ต้องการ" เพื่อคอมมิทเก็บการที่เราได้กระทำต่างๆ
$ git commit -m"Add app for lists, with deliberately failing unit test"
[master 7a77fd8] Add app for lists, with deliberately failing unit test
 Committer: jutamas <jutamas@jutamas.(none)>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:
    git config --global user.name "Your Name"
    git config --global user.email you@example.com
After doing this, you may fix the identity used for this commit with:
    git commit --amend --reset-author
 5 files changed, 18 insertions(+)
 create mode 100644 lists/__init__.py
 create mode 100644 lists/admin.py
 create mode 100644 lists/models.py
 create mode 100644 lists/tests.py
 create mode 100644 lists/tests.py~
 create mode 100644 lists/views.py 

Django’s MVC, URLs, and View Functions
    จะมีการทดสอบโปรแกรม 2 อย่าง คือ
          1. สามารถแก้ไข URL root ("/") ของเว็บไซต์ได้หรือไม่
          2. สามารถทำให้ฟังก์ชั่น view return HTML
แก้ไขไฟล์ lists/tests.py ดังนี้

from django.core.urlresolvers import resolve
from django.test import TestCase
from lists.views import home_page #1
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/')  #2
        self.assertEqual(found.func, home_page)  #3

    จากนั้นรันไฟล์ test จะแสดงข้อความดังนี้ คือไม่สามารถ import ชื่อ home_page ได้
$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: lists.tests (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: lists.tests
Traceback (most recent call last):
  File "/usr/lib/python2.7/unittest/loader.py", line 252, in _find_tests
    module = self._get_module_from_name(name)
  File "/usr/lib/python2.7/unittest/loader.py", line 230, in _get_module_from_name
    __import__(name)
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 3, in <module>
    from lists.views import home_page #1
ImportError: cannot import name home_page
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
Destroying test database for alias 'default'... 

At Last! We Actually Write Some Application Code!
    คือจะ import home_page จากไฟล์ได้หรือไม่ โดยทำการแก้ไขไฟล์
lists/viws.py ดังนี้
from django.shortcuts import render
# Create your views here.
home_page = None 

    จากนั้นรันไฟล์ test จะแสดงผลดังนี้ คือ โปรแกรมต้องการจะหา path .s,j
$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: test_root_url_resolves_to_home_page_view (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 8, in test_root_url_resolves_to_home_page_view
    found = resolve('/')  #2
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 453, in resolve
    return get_resolver(urlconf).resolve(path)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 333, in resolve
    raise Resolver404({'tried': tried, 'path': new_path})
Resolver404: {u'path': '', u'tried': [[<RegexURLResolver <RegexURLPattern list> (admin:admin) ^admin/>]]}
----------------------------------------------------------------------
Ran 1 test in 0.012s
FAILED (errors=1)
Destroying test database for alias 'default'... 

urls.py
     Django จะเรียกใช้ไฟล์ urls.py เพื่อหา URL ที่นำมาใช้กับฟังก์ชั่น view โดยเปิดเข้าไปในไฟล์
superlists/urls.py. จะมีดังนี้

from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'superlists.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
)
    จากนั้นทำการ คอมเม้นโดยใส่เครื่องหมาย # หน้า url(r'^admin/', include(admin.site.urls)),
จากนั้นลบ # ออกจากurl(r'^$', 'superlists.views.home', name='home'),


urlpatterns = patterns('',
    # Examples:
    url(r'^$', 'superlists.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    # url(r'^admin/', include(admin.site.urls)),
)

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือไม่สามารถ import superlistsviews.home ได้ เนื่องจากไม่มี module superlists.views
$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: test_root_url_resolves_to_home_page_view (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 8, in test_root_url_resolves_to_home_page_view
    found = resolve('/')  #2
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 453, in resolve
    return get_resolver(urlconf).resolve(path)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 320, in resolve
    sub_match = pattern.resolve(new_path)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 222, in resolve
    return ResolverMatch(self.callback, args, kwargs, self.name)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 229, in callback
    self._callback = get_callable(self._callback_str)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/functional.py", line 32, in wrapper
    result = func(*args)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 103, in get_callable
    (lookup_view, mod_name))
ViewDoesNotExist: Could not import superlists.views.home. Parent module superlists.views does not exist.
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)
Destroying test database for alias 'default'...
 

แก้ไขไฟล์ superlists/urls.py.  ดังนี้
urlpatterns = patterns('',
    # Examples:
    url(r'^$', 'lists.views.home_page', name='home'),

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ ไม่สามารถ import ไฟล์ lists.vies.home_page  
$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: test_root_url_resolves_to_home_page_view (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 8, in test_root_url_resolves_to_home_page_view
    found = resolve('/')  #2
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 453, in resolve
    return get_resolver(urlconf).resolve(path)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 320, in resolve
    sub_match = pattern.resolve(new_path)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 222, in resolve
    return ResolverMatch(self.callback, args, kwargs, self.name)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 229, in callback
    self._callback = get_callable(self._callback_str)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/functional.py", line 32, in wrapper
    result = func(*args)
  File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py", line 112, in get_callable
    (mod_name, func_name))
ViewDoesNotExist: Could not import lists.views.home_page. View is not callable.

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (errors=1)
Destroying test database for alias 'default'...

แก้ไขไฟล์ lists/views.py ดังนี้ โดยกำหนดฟังก์ชั่น home_page ขึ้นมา
from django.shortcuts import render
# Create your views here.
def home_page():
    pass 

ทำการรันไฟล์ test จะแสดงผลดังนี้คือ สามารถรันโปรแกรมผ่าน
$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Destroying test database for alias 'default'... 

ใช้คำสั่ง git diff เพื่อดูว่ามีการแก้ไขไฟล์ไหน แล้วแก้อะไรบ้าง
$ git diff
diff --git a/lists/tests.py b/lists/tests.py
index fce38d1..3b06997 100644
--- a/lists/tests.py
+++ b/lists/tests.py
@@ -1,6 +1,9 @@
+from django.core.urlresolvers import resolve
 from django.test import TestCase
+from lists.views import home_page #1
-class SmokeTest(TestCase):
+class HomePageTest(TestCase):
-    def test_bad_maths(self):
-        self.assertEqual(1 + 1, 3)
+    def test_root_url_resolves_to_home_page_view(self):
+        found = resolve('/')  #2
+        self.assertEqual(found.func, home_page)  #3
diff --git a/lists/tests.py~ b/lists/tests.py~
index 7ce503c..fce38d1 100644
--- a/lists/tests.py~
+++ b/lists/tests.py~
@@ -1,3 +1,6 @@
 from django.test import TestCase
:
 

ใช้คำสั่ง git commit -am  "First unit test and url mapping, dummy view" จะทำให้คอมมิทโดยอัตโนมัติเลย
$ git commit -am"First unit test and url mapping, dummy view"
[master c29b8f9] First unit test and url mapping, dummy view
 Committer: jutamas <jutamas@jutamas.(none)>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:
    git config --global user.name "Your Name"
    git config --global user.email you@example.com
After doing this, you may fix the identity used for this commit with:
    git commit --amend --reset-author
 5 files changed, 16 insertions(+), 6 deletions(-)
 rewrite superlists/urls.pyc (99%)
 

Unit Testing a View
แก้ไขไฟล์ lists/tests.py.  ดังนี้

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()  #1
        response = home_page(request)  #2
        self.assertTrue(response.content.startswith(b'<html>'))  #3
        self.assertIn(b'<title>To-Do lists</title>', response.content)  #4
        self.assertTrue(response.content.endswith(b'</html>'))  #5

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ homepage() ไม่มี arguments โดยต้องการ 1 arguments
$ python manage.py test
Creating test database for alias 'default'...
E.
======================================================================
ERROR: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 17, in test_home_page_returns_correct_html
    response = home_page(request)  #2
TypeError: home_page() takes no arguments (1 given)
----------------------------------------------------------------------
Ran 2 tests in 0.002s
FAILED (errors=1)
Destroying test database for alias 'default'...
 

The Unit-Test/Code Cycle

แก้ไขไฟล์ lists/views.py.  ดังนี้

def home_page(request):
    pass
 

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ object ไม่มี attribute 'content'
$ python manage.py test
Creating test database for alias 'default'...
E.
======================================================================
ERROR: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 18, in test_home_page_returns_correct_html
    self.assertTrue(response.content.startswith(b'<html>'))  #3
AttributeError: 'NoneType' object has no attribute 'content'
----------------------------------------------------------------------
Ran 2 tests in 0.002s
FAILED (errors=1)
Destroying test database for alias 'default'...
 


แก้ไขไฟล์ lists/views.py. ดังนี้


 from django.http import HttpResponse
# Create your views here.
def home_page(request):
    return HttpResponse()

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ โปรแกรมจะต้องเริ่มต้นด้วย b'<html>' ก่อน

 $ python manage.py test
Creating test database for alias 'default'...
F.
======================================================================
FAIL: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 18, in test_home_page_returns_correct_html
    self.assertTrue(response.content.startswith(b'<html>'))  #3
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (failures=1)
Destroying test database for alias 'default'...

แก้ไขไฟล์ lists/views.py. โดยให้ส่งค่า <html> ดังนี้


 def home_page(request):
    return HttpResponse('<html>')

ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ ใน <title> To-Do lists</title> ไม่พบใน <html>

 $ python manage.py test
Creating test database for alias 'default'...
F.
======================================================================
FAIL: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 19, in test_home_page_returns_correct_html
    self.assertIn(b'<title>To-Do lists</title>', response.content)  #4
AssertionError: '<title>To-Do lists</title>' not found in '<html>'
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (failures=1)
Destroying test database for alias 'default'...


แก้ไขไฟล์ lists/views.py. โดยเพิ่ม title To-Do list ดังนี้


 from django.http import HttpResponse
# Create your views here.
def home_page(request):
    return HttpResponse('<html><title>To-Do lists</title>')


ทำการรันไฟล์ test จะแสดงผลดังนี้ คือ จะ false เนื่องจากจะต้อง return </html>
 $ python manage.py test
Creating test database for alias 'default'...
F.
======================================================================
FAIL: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jutamas/Desktop/summer/superlists/lists/tests.py", line 20, in test_home_page_returns_correct_html
    self.assertTrue(response.content.endswith(b'</html>'))  #5
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (failures=1)
Destroying test database for alias 'default'...


แก้ไขไฟล์ lists/views.py. ดังนี้


 def home_page(request):
    return HttpResponse('<html><title>To-Do lists</title></html>')

ทำการรันไฟล์ test แสดงผลดังนี้ คือ มีการรันผ่าน
$ python manage.py test
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
Destroying test database for alias 'default'...

ต่อไปทำการรันไฟล์ functional_tests.py เพื่อทดสอบการทำงานของโปรแกรมจะแสดงผลดังนี้ คือ เสร็จสิ้นการทดสอบ


$ python functional_tests.py
F
======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "functional_tests.py", line 20, in test_can_start_a_list_and_retrieve_it_later
    self.fail('Finish the test!')  #6
AssertionError: Finish the test!
----------------------------------------------------------------------
Ran 1 test in 4.900s
FAILED (failures=1)

ใช้คำสั่ง git diff เพื่อดูการแก้ไขแต่ละไฟล์

 $ git diff
diff --git a/lists/tests.py b/lists/tests.py
index 3b06997..918c6a1 100644
--- a/lists/tests.py
+++ b/lists/tests.py
@@ -1,9 +1,20 @@
 from django.core.urlresolvers import resolve
 from django.test import TestCase
-from lists.views import home_page #1
+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('/')  #2
-        self.assertEqual(found.func, home_page)  #3
+        found = resolve('/')
+        self.assertEqual(found.func, home_page)
+
+
+    def test_home_page_returns_correct_html(self):
:

และทำการคอมมิทชื่อว่า "Basic view now returns minimal HTML"

 $ git commit -am"Basic view now returns minimal HTML"
[master 92a31ba] Basic view now returns minimal HTML
 Committer: jutamas <jutamas@jutamas.(none)>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:
    git config --global user.name "Your Name"
    git config --global user.email you@example.com
After doing this, you may fix the identity used for this commit with:
    git commit --amend --reset-author
 3 files changed, 23 insertions(+), 9 deletions(-)

ใช้คำสั่ง git log --oneline เพื่อดู version การคอมมิท

$ git log --oneline
92a31ba Basic view now returns minimal HTML
c29b8f9 First unit test and url mapping, dummy view
7a77fd8 Add app for lists, with deliberately failing unit test
d69c2f0 Fist FT specced out in comments, and now uses unittest
c7ec8f9 First commit: First FT and basic Django config










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

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