W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
測(cè)試客戶端是一個(gè) Python 類,它充當(dāng)虛擬 Web 瀏覽器,允許您測(cè)試視圖并以編程方式與 Django 驅(qū)動(dòng)的應(yīng)用程序交互。
你可以使用測(cè)試客戶端執(zhí)行以下操作:
GET
? 和 ?POST
?請(qǐng)求并觀察響應(yīng)——從低級(jí) HTTP(結(jié)果頭和狀態(tài)碼)到頁面內(nèi)容,應(yīng)有盡有。請(qǐng)注意,測(cè)試客戶端并不是要取代 ?Selenium
? 或其他“瀏覽器內(nèi)”框架。Django 的測(cè)試客戶端有不同的側(cè)重點(diǎn)。簡(jiǎn)而言之:
Selenium
?等瀏覽器內(nèi)框架來測(cè)試呈現(xiàn)的 HTML 和網(wǎng)頁的行為,即 JavaScript 功能。 Django 還為這些框架提供了特殊支持一個(gè)全面的測(cè)試套件應(yīng)該使用這兩種測(cè)試類型的組合。
要使用測(cè)試客戶端,請(qǐng)實(shí)例化 ?django.test.Client
? 并檢索網(wǎng)頁:
>>> 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
b'<!DOCTYPE html...'
如本例所示,你可以從 Python 交互式解釋器的會(huì)話中實(shí)例化 ?Client
?。
請(qǐng)注意測(cè)試客戶端如何工作的一些重要事項(xiàng):
>>> c.get('/login/')
這是錯(cuò)誤的:
>>> c.get('https://www.example.com/login/')
測(cè)試客戶端無法檢索不是由您的 Django 項(xiàng)目提供支持的網(wǎng)頁。 如果您需要檢索其他網(wǎng)頁,請(qǐng)使用 Python 標(biāo)準(zhǔn)庫模塊,例如 ?urllib
?。
ROOT_URLCONF
?設(shè)置指向的任何 ?URLconf
?。enforce_csrf_checks
?參數(shù):>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)
使用 ?django.test.Client
? 類發(fā)出請(qǐng)求。
它在構(gòu)造時(shí)不需要任何參數(shù)。然而,你可以使用關(guān)鍵字參數(shù)來指定一些默認(rèn)頭信息。例如,這將在每個(gè)請(qǐng)求中發(fā)送一個(gè) ?User-Agent
? HTTP 頭:
>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
傳遞給 ?get()
?、?post()
? 等方法的 ?extra
? 關(guān)鍵字參數(shù)的值,優(yōu)先于傳遞給類構(gòu)造函數(shù)的默認(rèn)值。
?enforce_csrf_checks
?參數(shù)可用于測(cè)試 CSRF 保護(hù)。
?json_encoder
?參數(shù)允許為 ?post()
? 中描述的 JSON 序列化設(shè)置一個(gè)自定義 JSON 編碼器。
?raise_request_exception
?參數(shù)允許控制是否在請(qǐng)求過程中引出的異常也應(yīng)該在測(cè)試中引出。默認(rèn)值為 ?True
?。
一旦有了 Client 實(shí)例,就可以調(diào)用以下任何一種方法:
對(duì)提供的 ?path
?上發(fā)出 ?GET
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象,如下所述。
?data
?字典中的鍵值對(duì)用于創(chuàng)建 ?GET
?數(shù)據(jù)有效載荷。例如:
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7})
產(chǎn)生等效的 GET 請(qǐng)求:
/customers/details/?name=fred&age=7
?extra
?關(guān)鍵詞參數(shù)可以用來指定請(qǐng)求中要發(fā)送的頭信息。例如:
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
... HTTP_ACCEPT='application/json')
將 HTTP 頭 ?HTTP_ACCEPT
?發(fā)送到 ?detail
?視圖,這是測(cè)試使用 ?django.http.HttpRequest.accepts()
? 方法的代碼路徑的好方法。
如果你已經(jīng)有了 URL 編碼形式的 ?GET
?參數(shù),你可以使用該編碼代替使用數(shù)據(jù)參數(shù)。例如,之前的 ?GET
?請(qǐng)求也可以改成:
>>> c = Client()
>>> c.get('/customers/details/?name=fred&age=7')
如果你提供的 URL 同時(shí)包含編碼的 ?GET
?數(shù)據(jù)和數(shù)據(jù)參數(shù),數(shù)據(jù)參數(shù)將優(yōu)先。
如果將 ?follow
?設(shè)置為 ?True
?,客戶端將遵循所有重定向,并且將在響應(yīng)對(duì)象中設(shè)置 ?redirect_chain
?屬性,該屬性是包含中間 URL 和狀態(tài)碼的元組。
如果你有一個(gè) URL ?/redirect_me/
?,重定向到? /next/
?,再重定向到 ?/final/
?,這是你會(huì)看到的:
>>> response = c.get('/redirect_me/', follow=True)
>>> response.redirect_chain
[('http://testserver/next/', 302), ('http://testserver/final/', 302)]
如果你把 ?secure
?設(shè)置為 ?True
?,則客戶端將模擬 HTTPS 請(qǐng)求。
在提供的 ?path
?上發(fā)出一個(gè) ?POST
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象,如下所述。
?data
?字典中的鍵值對(duì)用于提交 ?POST
?數(shù)據(jù)。例如:
>>> c = Client()
>>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
這將產(chǎn)生對(duì)這個(gè) URL 的 ?POST
?請(qǐng)求:
/login/
且具有此 ?POST
?數(shù)據(jù):
name=fred&passwd=secret
如果你提供 ?application/json
? 為 ?content_type
?,則如果 ?data
?是一個(gè)字典、列表或元組時(shí),使用 ?json.dumps()
? 進(jìn)行序列化。序列化默認(rèn)是通過 ?DjangoJSONEncoder
?,可以通過為 Client 提供 ?json_encoder
?參數(shù)覆蓋。這個(gè)序列化也會(huì)發(fā)生在 ?put()
?、?patch()
? 和 ?delete()
? 請(qǐng)求中。
如果你要提供任何其他的 ?content_type
?(例如 ?text/xml
? 用于 XML 有效載荷),使用HTTP ?Content-Type
? 頭中的 ?content_type
?,?data
?的內(nèi)容在 ?POST
?請(qǐng)求中按原樣發(fā)送。
如果你沒有為 ?content_type
?提供一個(gè)值,data 中的值將以 ?multipart/form-data
? 的內(nèi)容類型進(jìn)行傳輸。在這種情況下,data 中的鍵值對(duì)將被編碼為多部分消息,并用于創(chuàng)建 ?POST
?數(shù)據(jù)有效載荷。
要為一個(gè)給定的鍵提交多個(gè)值——例如,要指定 ?<select multiple>
? 的選擇——為所需鍵提供一個(gè)列表或元組的值。例如,這個(gè) ?data
?的值將為名為 ?choices
?的字段提交三個(gè)選擇值:
{'choices': ('a', 'b', 'd')}
提交文件是一種特殊情況。要 ?POST
?一個(gè)文件,你只需要提供文件字段名作為鍵,以及你想上傳的文件的文件句柄作為值。例如:
>>> c = Client()
>>> with open('wishlist.doc', 'rb') as fp:
... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
這里的?attachment
?可以修改成你處理代碼時(shí)想要的名稱
你也可以提供任何類似于文件的對(duì)象(例如 ?StringIO
?或 ?BytesIO
?)作為文件句柄。如果你要上傳到 ?ImageField
?,這個(gè)對(duì)象需要一個(gè)可以通過 ?validate_image_file_extension
?驗(yàn)證器的 ?name
?屬性。例如:
>>> from io import BytesIO
>>> img = BytesIO(b'mybinarydata')
>>> img.name = 'myimage.jpg'
請(qǐng)注意,如果你想在多次調(diào)用 ?post()
? 時(shí)使用同一個(gè)文件句柄,那么你需要在兩次調(diào)用之間手動(dòng)重置文件指針。最簡(jiǎn)單的方法是在向 ?post()
? 提供文件后手動(dòng)關(guān)閉文件,如上所示。
你還應(yīng)確保文件的打開方式允許數(shù)據(jù)被讀取。如果你的文件包含二進(jìn)制數(shù)據(jù),如圖像,這意味著你需要以 ?rb
? (讀取二進(jìn)制)模式打開文件。
?extra
?參數(shù)的作用與 ?Client.get()
? 相同。
如果你用 ?POST
?請(qǐng)求的 URL 包含編碼參數(shù),這些參數(shù)將在 ?request.GET
? 數(shù)據(jù)中提供。例如,如果你要請(qǐng)求:
>>> c.post('/login/?visitor=true', {'name': 'fred', 'passwd': 'secret'})
處理這個(gè)請(qǐng)求的視圖可以詢問 ?request.POST
? 來檢索用戶名和密碼,也可以詢問 ?request.GET
?來確定該用戶是否是訪客。
如果將 ?follow
?設(shè)置為 ?True
?,客戶端將遵循所有重定向,并且將在響應(yīng)對(duì)象中設(shè)置 ?redirect_chain
?屬性,該屬性是包含中間 URL 和狀態(tài)碼的元組。
如果你把 ?secure
?設(shè)置為 ?True
?,則客戶端將模擬 HTTPS 請(qǐng)求。
在提供的 ?path
?上發(fā)出一個(gè) ?HEAD
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象。這個(gè)方法的工作原理和 ?Client.get()
? 一樣,包括 ?follow
?、?secure
?和 ?extra
?參數(shù),只是它不返回消息主體。
在提供的 ?path
?上發(fā)出一個(gè) ?OPTIONS
?請(qǐng)求并返回一個(gè) ?Response
?對(duì)象。用于測(cè)試 RESTful 接口。
當(dāng)提供 ?data
?時(shí),它將被用作請(qǐng)求主體并且 ?Content-Type
? 頭被設(shè)置為 ?content_type
?。
?follow
?、 ?secure
?和 ?extra
?參數(shù)的作用與 ?Client.get()
? 相同。
在提供的 ?path
?上發(fā)出一個(gè) ?PUT
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象。用于測(cè)試 RESTful 接口。
當(dāng)提供 ?data
?時(shí),它將被用作請(qǐng)求主體并且 ?Content-Type
? 頭被設(shè)置為 ?content_type
?。
?follow
?、 ?secure
?和 ?extra
?參數(shù)的作用與 ?Client.get()
? 相同。
在提供的 ?path
?上發(fā)出一個(gè) ?PATCH
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象。用于測(cè)試 RESTful 接口。
?follow
?、 ?secure
?和 ?extra
?參數(shù)的作用與 ?Client.get()
? 相同。
在提供的 ?path
?上發(fā)出一個(gè) ?DELETE
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象。用于測(cè)試 RESTful 接口。
當(dāng)提供 ?data
?時(shí),它將被用作請(qǐng)求主體并且 ?Content-Type
? 頭被設(shè)置為 ?content_type
?。
?follow
?、 ?secure
?和 ?extra
?參數(shù)的作用與 ?Client.get()
?相同。
在提供的 ?path
?上發(fā)出一個(gè) ?TRACE
?請(qǐng)求,并返回一個(gè) ?Response
?對(duì)象。用于模擬診斷探針。
與其他請(qǐng)求方法不同,為了符合 RFC 7231#section-4.3.8 的要求,不提供 ?data
?作為關(guān)鍵字參數(shù),該 RFC 要求跟蹤請(qǐng)求不能有主體。
?follow
?、 ?secure
?和 ?extra
?參數(shù)的作用與 ?Client.get()
? 相同。
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),并且你需要處理登錄用戶的問題,你可以使用測(cè)試客戶端的 ?login()
? 方法來模擬用戶登錄網(wǎng)站的效果。
調(diào)用此方法后,測(cè)試客戶端將擁有通過任何可能構(gòu)成視圖一部分的基于登錄的測(cè)試所需的所有 cookie 和會(huì)話數(shù)據(jù)。
?credentials
?參數(shù)的格式取決于你使用的 認(rèn)證后端 (這是由你的 ?AUTHENTICATION_BACKENDS
?配置)。如果你使用的是 Django 提供的標(biāo)準(zhǔn)認(rèn)證后端(?ModelBackend
?),?credentials
?應(yīng)該是用戶的用戶名和密碼,并作為關(guān)鍵字參數(shù)提供:
>>> c = Client()
>>> c.login(username='fred', password='secret')
# Now you can access a view that's only available to logged-in users.
如果你使用的是不同的認(rèn)證后端,這個(gè)方法可能需要不同的憑證。它需要你的后端 ?authenticate()
? 方法所需要的任何憑證。
如果憑證被接受且登錄成功,則 ?login()
? 返回 ?True
?。
最后,在使用這個(gè)方法之前,你需要記得創(chuàng)建用戶賬戶。正如我們上面所解釋的,測(cè)試運(yùn)行器是使用測(cè)試數(shù)據(jù)庫執(zhí)行的,默認(rèn)情況下,數(shù)據(jù)庫中不包含用戶。因此,在生產(chǎn)站點(diǎn)上有效的用戶賬戶在測(cè)試條件下將無法工作。你需要?jiǎng)?chuàng)建用戶作為測(cè)試套件的一部分--無論是手動(dòng)創(chuàng)建(使用 Django 模型 API)還是使用測(cè)試夾具。記住,如果你想讓你的測(cè)試用戶有一個(gè)密碼,你不能直接通過設(shè)置密碼屬性來設(shè)置用戶的密碼——你必須使用 ?set_password()
? 函數(shù)來存儲(chǔ)一個(gè)正確的哈希密碼。或者,你可以使用 ?create_user()
? 輔助方法來創(chuàng)建一個(gè)具有正確哈希密碼的新用戶。
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),你可以使用 ?force_login()
? 方法來模擬用戶登錄網(wǎng)站的效果。當(dāng)測(cè)試需要用戶登錄,而用戶如何登錄的細(xì)節(jié)并不重要時(shí),可以使用這個(gè)方法代替 ?login()
?。
與 ?login()
? 不同的是,這個(gè)方法跳過了認(rèn)證和驗(yàn)證步驟:不活躍的用戶(?is_active=False
?)被允許登錄,并且不需要提供用戶憑證。
用戶的 ?backend
?屬性將被設(shè)置為 ?backend
?參數(shù)的值(應(yīng)該是一個(gè)點(diǎn)分隔 Python 路徑字符串),如果沒有提供值,則設(shè)置為 ?settings.AUTHENTICATION_BACKENDS[0]
??login()
?調(diào)用的 ?authenticate()
? 函數(shù)通常會(huì)對(duì)用戶進(jìn)行注釋。
這個(gè)方法比? login()
? 快,因?yàn)樗@過了昂貴的密碼散列算法。另外,你也可以通過 在測(cè)試時(shí)使用較弱的哈希算法 來加快 login() 速度。
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),?logout()
? 方法可以用來模擬用戶注銷網(wǎng)站的效果。
調(diào)用此方法后,測(cè)試客戶端的所有 cookie 和會(huì)話數(shù)據(jù)都會(huì)被清除為默認(rèn)值。隨后的請(qǐng)求將看起來來自一個(gè) ?AnonymousUser
?。
?get()
?和 ?post()
? 方法都會(huì)返回一個(gè) ?Response
?對(duì)象,這個(gè) ?Response
?對(duì)象與 Django 視圖返回的 ?HttpResponse
?對(duì)象是 不 一樣的;測(cè)試響應(yīng)對(duì)象有一些額外的數(shù)據(jù),對(duì)測(cè)試代碼驗(yàn)證很有用。
具體來說,?Response
?對(duì)象具有以下屬性:
?client
?:用于發(fā)出請(qǐng)求并得到響應(yīng)的測(cè)試客戶端。
?content
?:以字節(jié)字符串形式的響應(yīng)主體。 這是視圖或任何錯(cuò)誤消息所呈現(xiàn)的最終頁面內(nèi)容。
?context
?:模板 ?Context
?實(shí)例,用于渲染產(chǎn)生響應(yīng)內(nèi)容的模板。如果渲染的頁面使用了多個(gè)模板,那么 ?context
?將是一個(gè)按渲染順序排列的 ?Context
?對(duì)象列表。無論在渲染過程中使用了多少模板,你都可以使用 ?[]
? 操作符來檢索上下文值。例如,上下文變量 ?name
?可以使用:
>>> response = client.get('/foo/')
>>> response.context['name']
'Arthur'
?exc_info
?:一個(gè)由三個(gè)值組成的元組,它提供了關(guān)于在視圖期間發(fā)生的未處理異常(如果有)的信息。值是(type,value,traceback),與 Python 的 ?sys.exc_info()
? 返回的值相同。它們的含義是:
type
?:異常的類型。value
?:異常的實(shí)例。traceback
?:一個(gè)追溯對(duì)象,在最初發(fā)生異常的地方封裝了調(diào)用堆棧。如果沒有發(fā)生異常,那么 ?exc_info
?將是 ?None
?。
?json(**kwargs)
?:解析為 JSON 的響應(yīng)主體。額外的關(guān)鍵字參數(shù)傳遞給? json.loads()
?。例如:
>>> response = client.get('/foo/')
>>> response.json()['name']
'Arthur'
如果 ?Content-Type
? 頭不是 ?application/json
?,那么在試圖解析響應(yīng)時(shí)將會(huì)出現(xiàn)一個(gè) ?ValueError
?。
?request
?:激發(fā)響應(yīng)的請(qǐng)求數(shù)據(jù)。
?wsgi_request
?:由生成響應(yīng)的測(cè)試處理程序生成的 ?WSGIRequest
?實(shí)例。
?status_code
?:整數(shù)形式的響應(yīng) HTTP 狀態(tài)。
?templates
?:用于渲染最終內(nèi)容的 ?Template
?實(shí)例列表,按渲染順序排列。對(duì)于列表中的每個(gè)模板,如果模板是從文件中加載的,則使用 ?template.name
? 獲得模板的文件名。(名字是一個(gè)字符串,如 ?admin/index.html
?。)
?resolver_match
?:響應(yīng)的 ?ResolverMatch
?的實(shí)例。你可以使用 ?func
?屬性,例如,驗(yàn)證服務(wù)于響應(yīng)的視圖:
# my_view here is a function based view
self.assertEqual(response.resolver_match.func, my_view)
# class-based views need to be compared by name, as the functions
# generated by as_view() won't be equal
self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__)
如果找不到給定的 URL,訪問這個(gè)屬性會(huì)引發(fā)一個(gè) ?Resolver404
?異常。
和普通的響應(yīng)一樣,你也可以通過 ?HttpResponse.headers
? 訪問頭信息。例如,你可以使用 ?response.headers['Content-Type']
? 來確定一個(gè)響應(yīng)的內(nèi)容類型。
如果你把測(cè)試客戶端指向一個(gè)會(huì)引發(fā)異常的視圖,并且 ?Client.raise_request_exception
? 是 ?True
?,那么這個(gè)異常將在測(cè)試用例中可見。然后你可以使用標(biāo)準(zhǔn)的 ?try ... except
? 塊或 ?assertRaises()
? 來測(cè)試異常。
測(cè)試客戶端看不到的異常只有 ?Http404
?、?PermissionDenied
?、?SystemExit
?和 ?SuspiciousOperation
?。Django 在內(nèi)部捕獲這些異常,并將其轉(zhuǎn)換為相應(yīng)的 HTTP 響應(yīng)代碼。在這些情況下,你可以在測(cè)試中檢查 ?response.status_code
?。
如果 ?Client.raise_request_exception
? 為 ?False
?,測(cè)試客戶端將返回一個(gè) 500 的響應(yīng),就像返回給瀏覽器一樣。響應(yīng)有屬性 ?exc_info
?來提供關(guān)于未處理的異常的信息。
測(cè)試客戶端是有狀態(tài)的。如果一個(gè)響應(yīng)返回一個(gè) cookie,那么這個(gè) cookie 將被存儲(chǔ)在測(cè)試客戶端,并與所有后續(xù)的 ?get()
? 和 ?post()
? 請(qǐng)求一起發(fā)送。
不遵循這些 cookie 的過期策略。如果你希望 cookie 過期,請(qǐng)手動(dòng)刪除它或創(chuàng)建一個(gè)新的 Client 實(shí)例(這將有效地刪除所有 cookie)。
測(cè)試客戶端有兩個(gè)屬性,存儲(chǔ)持久化的狀態(tài)信息。你可以作為測(cè)試條件的一部分來訪問這些屬性。
?Client.cookies
?:一個(gè) Python ?SimpleCookie
?對(duì)象,包含所有客戶端 cookie 的當(dāng)前值。
?Client.session
?:一個(gè)類似字典的對(duì)象,包含會(huì)話信息。要修改會(huì)話然后保存,必須先將其存儲(chǔ)在一個(gè)變量中(因?yàn)槊看卧L問該屬性時(shí)都會(huì)創(chuàng)建一個(gè)新的 ?SessionStore
?):
def test_something(self):
session = self.client.session
session['somekey'] = 'test'
session.save()
在測(cè)試支持國(guó)際化和本地化的應(yīng)用程序時(shí),你可能想為測(cè)試客戶端請(qǐng)求設(shè)置語言。這樣做的方法取決于 ?LocaleMiddleware
?是否啟用。
如果啟用了中間件,可以通過創(chuàng)建一個(gè)名為 ?LANGUAGE_COOKIE_NAME
?的 cookie 來設(shè)置語言,其值為語言代碼:
from django.conf import settings
def test_language_using_cookie(self):
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'fr'})
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
或在請(qǐng)求中加入 ?Accept-Language
? HTTP 頭:
def test_language_using_header(self):
response = self.client.get('/', HTTP_ACCEPT_LANGUAGE='fr')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
如果中間件沒有啟用,可以使用 ?translation.override()
? 設(shè)置活動(dòng)語言:
from django.utils import translation
def test_language_using_override(self):
with translation.override('fr'):
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
以下是使用測(cè)試客戶端進(jìn)行的單元測(cè)試:
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
# Every test needs a client.
self.client = Client()
def test_details(self):
# Issue a GET request.
response = self.client.get('/customer/details/')
# Check that the response is 200 OK.
self.assertEqual(response.status_code, 200)
# Check that the rendered context contains 5 customers.
self.assertEqual(len(response.context['customers']), 5)
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: