自強(qiáng)學(xué)堂Django一系列教程,前面的例子都是我們寫好代碼后,運(yùn)行開發(fā)服務(wù)器,在瀏覽器上自己點(diǎn)擊測(cè)試,看寫的代碼是否正常,但是這樣做很麻煩,因?yàn)橐院笕绻懈膭?dòng),可能會(huì)影響以前本來正常的功能,這樣以前的功能又得測(cè)試一遍,非常不方便,Django中有完善的單元測(cè)試,我們可以對(duì)開發(fā)的每一個(gè)功能進(jìn)行單元測(cè)試,這樣只要運(yùn)行一個(gè)命令 python manage.py test,就可以測(cè)試功能是否正常。一言以蔽之,測(cè)試就是檢查代碼是否按照自己的預(yù)期那樣運(yùn)行。 測(cè)試驅(qū)動(dòng)開發(fā): 有時(shí)候,我們知道自己需要的功能(結(jié)果),并不知道代碼如何書寫,這時(shí)候就可以利用測(cè)試驅(qū)動(dòng)開發(fā)(Test Driven Development),先寫出我們期待得到的結(jié)果(把測(cè)試代碼先寫出來),再去完善代碼,直到不報(bào)錯(cuò),我們就完成了。 《改善Python的91個(gè)建議》一書中說:單元測(cè)試絕不是浪費(fèi)時(shí)間的無用功,它是高質(zhì)量代碼的保障之一,在軟件開發(fā)的一節(jié)中值得投入精力和時(shí)間去把好這一關(guān)。
1. Python 中 單元測(cè)試簡(jiǎn)介:
下面是一個(gè) Python的單元測(cè)試簡(jiǎn)單的例子: 假如我們開發(fā)一個(gè)除法的功能,有的同學(xué)可能覺得很簡(jiǎn)單,代碼是這樣的: 1 2 | def division_funtion(x, y):
return x / y
|
但是這樣寫究竟對(duì)還是不對(duì)呢,有些同學(xué)可以在代碼下面這樣測(cè)試: 1 2 3 4 5 6 7 8 | def division_funtion(x, y):
return x / y
if __name__ == '__main__':
print division_funtion(2, 1)
print division_funtion(2, 4)
print division_funtion(8, 3)
|
但是這樣運(yùn)行后得到的結(jié)果,自己每次都得算一下去核對(duì)一遍,很不方便,Python中有 unittest 模塊,可以很方便地進(jìn)行測(cè)試,詳情可以文章最后的鏈接,看官網(wǎng)文檔的詳細(xì)介紹。
下面是一個(gè)簡(jiǎn)單的示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import unittest
def division_funtion(x, y):
return x / y
class TestDivision(unittest.TestCase):
def test_int(self):
self.assertEqual(division_funtion(9, 3), 3)
def test_int2(self):
self.assertEqual(division_funtion(9, 4), 2.25)
def test_float(self):
self.assertEqual(division_funtion(4.2, 3), 1.4)
if __name__ == '__main__':
unittest.main()
|
我簡(jiǎn)單地寫了三個(gè)測(cè)試示例(不一定全面,只是示范,比如沒有考慮除數(shù)是0的情況),運(yùn)行后發(fā)現(xiàn): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | F.F
======================================================================
FAIL: test_float (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/tu/YunPan/mydivision.py", line 16, in test_float
self.assertEqual(division_funtion(4.2, 3), 1.4)
AssertionError: 1.4000000000000001 != 1.4
======================================================================
FAIL: test_int2 (__main__.TestDivision)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/tu/YunPan/1.py", line 13, in test_int2
self.assertEqual(division_funtion(9, 4), 2.25)
AssertionError: 2 != 2.25
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=2)
|
汗!發(fā)現(xiàn)了沒,竟然兩個(gè)都失敗了,測(cè)試發(fā)現(xiàn):
4.2除以3 等于 1.4000000000000001 不等于期望值 1.4 9除以4等于2,不等于期望的 2.25 下面我們就是要修復(fù)這些問題,再次運(yùn)行測(cè)試,直到運(yùn)行不報(bào)錯(cuò)為止。 譬如根據(jù)實(shí)際情況,假設(shè)我們只需要保留到小數(shù)點(diǎn)后6位,可以這樣改: 1 2 | def division_funtion(x, y):
return round(float(x) / y, 6)
|
再次運(yùn)行就不報(bào)錯(cuò)了: 1 2 3 4 5 | ...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
|
Python 單元測(cè)試 官方文檔: Python 2 (https://docs./2/library/unittest.html) Python 3 (https://docs./3/library/unittest.html)
2. Django 中 單元測(cè)試:(不斷完善中,后期會(huì)增加對(duì)前面講解的內(nèi)容的測(cè)試)
2.1 簡(jiǎn)單測(cè)試?yán)樱?/strong>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="lion", sound="roar")
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
lion = Animal.objects.get(name="lion")
cat = Animal.objects.get(name="cat")
self.assertEqual(lion.speak(), 'The lion says "roar"')
self.assertEqual(cat.speak(), 'The cat says "meow"')
|
這個(gè)例子是測(cè)試myapp.models 中的 Animal 類相關(guān)的方法功能。 2.2 用代碼訪問網(wǎng)址的方法: 1 2 3 4 5 6 7 8 | >>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
'<!DOCTYPE html...'
|
我們可以用 django.test.Client 的實(shí)例來實(shí)現(xiàn) get 或 post 內(nèi)容,檢查一個(gè)網(wǎng)址返回的網(wǎng)頁(yè)源代碼。
默認(rèn)情況下CSRF檢查是被禁用的,如果測(cè)試需要,可以用下面的方法: 1 2 | >>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)
|
使用 csrf_client 這個(gè)實(shí)例進(jìn)行請(qǐng)求即可。 指定瀏覽USER-AGENT:
1 | >>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
|
模擬post上傳附件: 1 2 3 4 5 | from django.test import Client
c = Client()
with open('wishlist.doc') as fp:
c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
|
測(cè)試網(wǎng)頁(yè)返回狀態(tài): 1 2 3 4 5 6 7 8 9 10 | from django.test import TestCase
class SimpleTest(TestCase):
def test_details(self):
response = self.client.get('/customer/details/')
self.assertEqual(response.status_code, 200)
def test_index(self):
response = self.client.get('/customer/index/')
self.assertEqual(response.status_code, 200)
|
我們用 self.client 即可,不用 client = Client() 這樣實(shí)例化,更方便,我們還可以繼承 Client,添加一些其它方法: 1 2 3 4 5 6 7 8 9 10 11 12 | from django.test import TestCase, Client
class MyTestClient(Client):
# Specialized methods for your environment
...
class MyTest(TestCase):
client_class = MyTestClient
def test_my_stuff(self):
# Here self.client is an instance of MyTestClient...
call_some_test_code()
|
定制 self.client 的方法: 1 2 3 4 5 6 7 8 9 10 11 | from django.test import Client, TestCase
class MyAppTests(TestCase):
def setUp(self):
super(MyAppTests, self).setUp()
self.client = Client(enforce_csrf_checks=True)
def test_home(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
|
|