ทำการสร้าง directory “functional_tests” ซึ่งภายในจะมีไฟล์ "__init__.py” ด้วยคำสั่ง
$ mkdir functional_tests
$ touch functional_tests/__init__.pyและทำการ move functional tests เข้าไปใน directory ที่เราสร้างขึ้น และเปลี่ยนชื่อเป็น tests.py ด้วยคำสั่ง
$ git mv functional_tests.py functional_tests/tests.py
$ git statusเมื่อรัน tree จะได้
.
├── db.sqlite3
├── functional_tests
│ ├── __init__.py
│ └── tests.py
├── lists
│ ├── admin.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_item_text.py
│ │ ├── __init__.py
│ │ └── __pycache__
│ ├── models.py
│ ├── __pycache__
│ ├── templates
│ │ └── home.html
│ ├── tests.py
│ └── views.py
├── manage.py
└── superlists
├── __init__.py
├── __pycache__
├── settings.py
├── urls.py
└── wsgi.pyจากนั้นทำการ แก้ไข functional_tests/tests.py และทำการเปลี่ยน
NewVisitorTest class เป็น
LiveServerTestCasefromdjango.testimportLiveServerTestCase
fromseleniumimportwebdriver
fromselenium.webdriver.common.keysimportKeys
classNewVisitorTest(LiveServerTestCase):
defsetUp(self):
[…]
จากนั้นทำการเปลี่ยน
localhost เป็น
live_server_url def
test_can_start_a_list_and_retrieve_it_later(self): # Edith has heard
about a cool new online to-do app. She goes# to check out its
homepage
self.browser.get(self.live_server_url)
ทดสอบรัน FT
ทำการ commit ไว้
$ git status #
functional_tests.py renamed + modified, new __init__.py
$ git add functional_tests
$ git diff --staged -M
$ git commit # msg eg "make functional_tests an app, use LiveServerTestCase"
จากนั้นทำการรัน Unit Test
และเราสามารถรันเฉพาะบางส่วนได้
ยกตัวอย่างให้รัน lists
Useful Commands Updated
python3 manage.py test functional_tests
python3 manage.py test lists
ทำการเข้าไปเพิ่มในส่วนท้ายของ
ไฟล์ functional_tests/tests.py ในส่วน
def
test_can_start_a_list_and_retrieve_it_later(self): ว่า
# Edith wonders whether the site will remember her list. Then she sees
# that the site has generate a unique URL for her -- there is some
# explanatory text to that effect.
self.fail('Finish the test!')
# She visits that URL - her to-do list is still there.
# Satisfied, she goes back to sleep
REST
Idea ของ
data structure คือ
Model-View-Controller (MVC) สำหรับในแต่ละ
lists และ
lists item ก็จะมี
URL ของมันเอง
/lists/<list identifier>/
ถ้าต้องการสร้าง
list ใหม่
ก็จะมี URL พิเศษที่มันสามารถ
accept POST request ได
/lists/new
ถ้าต้องการสร้าง
list item ใหม่จาก
list ที่มีอยู่แล้ว
เราจะมีการ separate URL
ซึ่งเราจะสามารถส่ง
POST request ได้
/lists/<list identifier>/add_item
**เราแค่จะใช้แนวคิดของ
REST มาใช้**
Implementing the New Design Using TDD
ให้มองหา
inputbox.send_keys('Buy peacock
feathers') ใน
functional_tests/tests.py. แล้วทำการแก้ไขดังนี้ inputbox.send_keys('Buy peacock feathers')
# When she hits enter, she is taken to a new URL,
# and now the page lists "1: Buy peacock feathers" as an item in a
# to-do list table
inputbox.send_keys(Keys.ENTER)
edith_list_url=self.browser.current_url
self.assertRegex(edith_list_url,'/lists/.+')
self.check_for_row_in_list_table('1: Buy peacock feathers')
# There is still a text box inviting her to add another item. She
[...]
และแก้ไขเพิ่มเติม
[...]
# The page updates again, and now shows both items on her list
self.check_for_row_in_list_table('2: Use peacock feathers to make a fly')
self.check_for_row_in_list_table('1: Buy peacock feathers')
# Now a new user, Francis, comes along to the site.
## We use a new browser session to make sure that no information
## of Edith's is coming through from cookies etc
self.browser.quit()
self.browser=webdriver.Firefox()
# Francis visits the home page. There is no sign of Edith's
# list
self.browser.get(self.live_server_url)
page_text=self.browser.find_element_by_tag_name('body').text
self.assertNotIn('Buy peacock feathers',page_text)
self.assertNotIn('make a fly',page_text)
# Francis starts a new list by entering a new item. He
# is less interesting than Edith...
inputbox=self.browser.find_element_by_id('id_new_item')
inputbox.send_keys('Buy milk')
inputbox.send_keys(Keys.ENTER)
# Francis gets his own unique URL
francis_list_url=self.browser.current_url
self.assertRegex(francis_list_url,'/lists/.+')
self.assertNotEqual(francis_list_url,edith_list_url)
# Again, there is no trace of Edith's list
page_text=self.browser.find_element_by_tag_name('body').text
self.assertNotIn('Buy peacock feathers',page_text)
self.assertIn('Buy milk',page_text)
# Satisfied, they both go back to sleep
และทดลองรัน FT
Commit ไว้ก่อน
$ git commit -a
Iterating Towards the New Design
ทำการเปลี่ยนแปลง
locationใน
lists/tests.py หาในส่วนของ
test_home_page_redirects_after_POSTself.assertEqual(response.status_code,302)
self.assertEqual(response['location'],'/lists/the-only-list-in-the-world/')
และทดลองรัน UT
ให้ทำการเข้าไปแก้ไข home_page
redirect ที่ lists/views.py.
defhome_page(request):
ifrequest.method=='POST':
Item.objects.create(text=request.POST['item_text'])
returnredirect('/lists/the-only-list-in-the-world/')
items=Item.objects.all()
returnrender(request,'home.html',{'items':items})
เมื่อรัน
Unit Test จะ
OK แต่ถ้ารัน
FT จะพบว่า
URL /the-only-list-in-the-world/
ไม่ได้มีอยู่จริง
Testing Views, Templates, and URLs Together with the Django Test Client
A New Test Class
ให้ทดลอง
test client โดยเปิด
lists/tests.py แล้ว
copy
method
test_home_page_displays_all_
list_items จาก HomePageTest แล้วทำการเปลี่ยนแปลงค่าเล็กน้อยclassListViewTest(TestCase):
deftest_displays_all_items(self):
Item.objects.create(text='itemey 1')
Item.objects.create(text='itemey 2')
response=self.client.get('/lists/the-only-list-in-the-world/')
self.assertContains(response,'itemey 1')
self.assertContains(response,'itemey 2')
ทดลอง รัน UT
จะพบว่า
404 != 200 ซึ่งเราจะต้องไปเพิ่มในส่วนของ
URLs
A New URL
เข้าไปที่
superlists/urls.py. แล้วทำการแก้ไขเพิ่มเติม
urlpatterns=patterns('',
url(r'^$','lists.views.home_page',name='home'),
url(r'^lists/the-only-list-in-the-world/$','lists.views.view_list',
name='view_list'
),
# url(r'^admin/', include(admin.site.urls)),
)
รัน
Unit Test อีกครั้ง
จะพบว่า
จะต้องมี view function เพิ่มเติมด้วย
A New View Function
ทำการสร้าง
dummy view function ใน
lists/views.py:
defview_list(request):
pass
ถ้าพอจำได้
การส่งค่า pass ธรรมดา
จะรันไม่ได้ เพราะต้องการ
HttpRespone ให้ทำการเพิ่มเติมส่วน
return
defview_list(request):
items=Item.objects.all()
returnrender(request,'home.html',{'items':items})
ทำการรัน
Unit Test
และทดลองรัน
FT
Green? Refactor
และเข้าไปทำการลบ
test_home_page_displays_all_list_items
method ใน lists/tests.py
และทดลองรัน
manage.py test listsA Separate Template for Viewing Lists
ทดลองรัน Test ใหม่เพื่อตรวจสอบการใช้งาน template และให้เข้าไปเพิ่มเติมใน
lists/views.py.classListViewTest(TestCase):
deftest_uses_list_template(self):
response=self.client.get('/lists/the-only-list-in-the-world/')
self.assertTemplateUsed(response,'list.html')
deftest_displays_all_items(self):
[...]
เมื่อทดลองรันทดสอบ
assert จะได้ว่า
AssertionError: False is not true : Template 'list.html' was not a template
used to render the response. Actual template(s) used: home.html
และให้เข้าไปแก้ไขใน
lists/views.py. อีกที
defview_list(request):
items=Item.objects.all()
returnrender(request,'list.html',{'items':items})
ทดลองรัน
UT จะได้
django.template.base.TemplateDoesNotExist: list.html
จากนั้นให้เข้าไปทำการสร้างไฟล์ใหม่
ที่ lists/templates/list.html
$ touch lists/templates/list.html
และทำการ
copy
ไฟล์
home.html
ไปเป็น
list.html
cp lists/templates/home.html lists/templates/list.html
ทดลองรัน
UT
จะเห็นว่ารันผ่าน
และทำการเปลี่ยนแปลงภายในไฟล์
lists/templates/home.html.
<body>
<h1>Start a new To-Do list</h1>
<formmethod="POST">
<inputname="item_text"id="id_new_item"placeholder="Enter a to-do item"/>
{% csrf_token %}
</form>
</body>
และทดลองรัน
UT อีกครั้ง
ก็ยังผ่านเหมือนเดิม
ให้เข้าไปแก้ไขใน lists/views.py.
defhome_page(request):
ifrequest.method=='POST':
Item.objects.create(text=request.POST['item_text'])
returnredirect('/lists/the-only-list-in-the-world/')
returnrender(request,'home.html')
รัน
UT อีกครั้งก็จะยัง
รันผ่านเหมือนเดิม และให้ทดลองรัน
FT
เรายังติดปัญหาที่
input ตัวที่
2 สามารถแก้ไขได้โดยเข้าไปที่
lists/templates/list.html:
<formmethod="POST"action="/">
และทดลองรัน
FT อีกครั้ง
โอเค
เราได้ URL ที่ไม่ซ้ำกันแล้วสำหรับ
1 list ของเรา
และต่อไปให้ทำการ git
commit ไว้ก่อน
$ git status # should show 4 changed files and 1 new file, list.html
$ git add lists/templates/list.html
$ git diff # should show we've simplified home.html,
# moved one test to a new class in lists/tests.py added a new view
# in views.py, and simplified home_page and made one addition to
# urls.py
$ git commit -a # add a message summarising the above, maybe something like
# "new URL, view and template to display lists"
Another URL and View for Adding List Items
A Test Class for New List Creation
เปิด lists/tests.py, แล้วทำการเพิ่มเติม classclassNewListTest(TestCase):
deftest_saving_a_POST_request(self):
self.client.post(
'/lists/new',
data={'item_text':'A new list item'}
)
self.assertEqual(Item.objects.count(),1)
new_item=Item.objects.first()
self.assertEqual(new_item.text,'A new list item')
deftest_redirects_after_POST(self):
response=self.client.post(
'/lists/new',
data={'item_text':'A new list item'}
)
self.assertEqual(response.status_code,302)
self.assertEqual(response['location'],'/lists/the-only-list-in-the-world/')
A URL and View for New List Creation
เปิด superlists/urls.py. และแก้ไขurlpatterns=patterns('',
url(r'^$','lists.views.home_page',name='home'),
url(r'^lists/the-only-list-in-the-world/$','lists.views.view_list',
name='view_list'
),
url(r'^lists/new$','lists.views.new_list',name='new_list'),
# url(r'^admin/',
include(admin.site.urls)),
)
เปิด lists/views.py. และแก้ไข
defnew_list(request):
returnredirect('/lists/the-only-list-in-the-world/')
จะได้ว่า
เข้าไป lists/views.py. และแก้ไขอีกครั้ง
defnew_list(request):
Item.objects.create(text=request.POST['item_text'])
returnredirect('/lists/the-only-list-in-the-world/')
จะพบว่า
AssertionError:
'http://testserver/lists/the-only-list-in-the-world/' !=
'/lists/the-only-list-in-the-world/'
-
http://testserver/lists/the-only-list-in-the-world/
? -----------------
+
/lists/the-only-list-in-the-world/
ให้เข้าไปแก้ไขที่
lists/tests.py. เปลี่ยนเป็น
AssertRedirectdeftest_redirects_after_POST(self):
response=self.client.post(
'/lists/new',
data={'item_text':'A new list item'}
)
self.assertRedirects(response,'/lists/the-only-list-in-the-world/')
ผลที่ได้
Ran 8 tests in 0.018s
OK
Removing Now-Redundant Code and Tests
เราสามารถ removeif request.method ==
'POST' ได้หรือเปล่า
(สิ่งที่ซับซ้อน)
เข้าไปใน
lists/views.py.defhome_page(request):
ผลที่ได้returnrender(request,'home.html')
Ran 8 tests in 0.017s
OK
และเรายังสามารถ remove
test_home_page_only_saves_
items_when_necessary test ได้อีกด้วยผลที่ได้
Ran 7 tests in 0.016s
OK

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