新聞中心
測試客戶端是一個 Python 類,它充當(dāng)虛擬 Web 瀏覽器,允許您測試視圖并以編程方式與 Django 驅(qū)動的應(yīng)用程序交互。

專注于為中小企業(yè)提供做網(wǎng)站、網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)寧強(qiáng)免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了數(shù)千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
你可以使用測試客戶端執(zhí)行以下操作:
- 模擬 URL 上的 ?
GET? 和 ?POST?請求并觀察響應(yīng)——從低級 HTTP(結(jié)果頭和狀態(tài)碼)到頁面內(nèi)容,應(yīng)有盡有。 - 查看重定向鏈(如果有的話),并檢查每個步驟的 URL 和狀態(tài)碼。
- 測試給定的請求是否由給定的包含某些值以及模板上下文的 Django 模板渲染。
請注意,測試客戶端并不是要取代 ?Selenium? 或其他“瀏覽器內(nèi)”框架。Django 的測試客戶端有不同的側(cè)重點(diǎn)。簡而言之:
- 使用 Django 的測試客戶端來確定要渲染的模板正確,并且模板已傳遞了正確的上下文數(shù)據(jù)
- 使用 ?
Selenium?等瀏覽器內(nèi)框架來測試呈現(xiàn)的 HTML 和網(wǎng)頁的行為,即 JavaScript 功能。 Django 還為這些框架提供了特殊支持
一個全面的測試套件應(yīng)該使用這兩種測試類型的組合。
概述和一個簡單的例子
要使用測試客戶端,請實例化 ?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'如本例所示,你可以從 Python 交互式解釋器的會話中實例化 ?Client?。
請注意測試客戶端如何工作的一些重要事項:
- 測試客戶端不需要運(yùn)行 Web 服務(wù)器。 事實上,它會運(yùn)行得很好,根本沒有運(yùn)行 Web 服務(wù)器! 那是因為它避免了 HTTP 的開銷,直接與 Django 框架打交道。 這有助于使單元測試快速運(yùn)行。
- 檢索頁面時,請記住指定 URL 的 路徑,而不是整個域。例如,這是正確的:
>>> c.get('/login/')這是錯誤的:
>>> c.get('https://www.example.com/login/')測試客戶端無法檢索不是由您的 Django 項目提供支持的網(wǎng)頁。 如果您需要檢索其他網(wǎng)頁,請使用 Python 標(biāo)準(zhǔn)庫模塊,例如 ?urllib?。
- 為了解析 URL,測試客戶端使用你的 ?
ROOT_URLCONF?設(shè)置指向的任何 ?URLconf?。 - 盡管上面的示例可以在 Python 交互式解釋器中運(yùn)行,但測試客戶端的某些功能,尤其是與模板相關(guān)的功能,僅在測試運(yùn)行時才可用。這樣做的原因是 Django 的測試運(yùn)行器執(zhí)行了一些黑魔法,以確定給定視圖加載了哪個模板。 這種黑魔法(本質(zhì)上是在內(nèi)存中修補(bǔ) Django 的模板系統(tǒng))僅在測試運(yùn)行期間發(fā)生。
- 默認(rèn)情況下,測試客戶端將禁用您的站點(diǎn)執(zhí)行的任何 CSRF 檢查。如果出于某種原因,您希望測試客戶端執(zhí)行 CSRF 檢查,您可以創(chuàng)建一個強(qiáng)制執(zhí)行 CSRF 檢查的測試客戶端實例。 為此,請在構(gòu)建客戶端時傳入 ?
enforce_csrf_checks?參數(shù):
>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)發(fā)出請求
使用 ?django.test.Client? 類發(fā)出請求。
class Client(enforce_csrf_checks=False, json_encoder=DjangoJSONEncoder, **defaults)
它在構(gòu)造時不需要任何參數(shù)。然而,你可以使用關(guān)鍵字參數(shù)來指定一些默認(rèn)頭信息。例如,這將在每個請求中發(fā)送一個 ?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ù)可用于測試 CSRF 保護(hù)。
?json_encoder?參數(shù)允許為 ?post()? 中描述的 JSON 序列化設(shè)置一個自定義 JSON 編碼器。
?raise_request_exception?參數(shù)允許控制是否在請求過程中引出的異常也應(yīng)該在測試中引出。默認(rèn)值為 ?True?。
一旦有了 Client 實例,就可以調(diào)用以下任何一種方法:
get(path, data=None, follow=False, secure=False, **extra)
對提供的 ?path?上發(fā)出 ?GET?請求,并返回一個 ?Response?對象,如下所述。
?data?字典中的鍵值對用于創(chuàng)建 ?GET?數(shù)據(jù)有效載荷。例如:
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7})產(chǎn)生等效的 GET 請求:
/customers/details/?name=fred&age=7?extra?關(guān)鍵詞參數(shù)可以用來指定請求中要發(fā)送的頭信息。例如:
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
... HTTP_ACCEPT='application/json')將 HTTP 頭 ?HTTP_ACCEPT?發(fā)送到 ?detail?視圖,這是測試使用 ?django.http.HttpRequest.accepts()? 方法的代碼路徑的好方法。
如果你已經(jīng)有了 URL 編碼形式的 ?GET?參數(shù),你可以使用該編碼代替使用數(shù)據(jù)參數(shù)。例如,之前的 ?GET?請求也可以改成:
>>> c = Client()
>>> c.get('/customers/details/?name=fred&age=7')如果你提供的 URL 同時包含編碼的 ?GET?數(shù)據(jù)和數(shù)據(jù)參數(shù),數(shù)據(jù)參數(shù)將優(yōu)先。
如果將 ?follow?設(shè)置為 ?True?,客戶端將遵循所有重定向,并且將在響應(yīng)對象中設(shè)置 ?redirect_chain?屬性,該屬性是包含中間 URL 和狀態(tài)碼的元組。
如果你有一個 URL ?/redirect_me/?,重定向到? /next/?,再重定向到 ?/final/?,這是你會看到的:
>>> response = c.get('/redirect_me/', follow=True)
>>> response.redirect_chain
[('http://testserver/next/', 302), ('http://testserver/final/', 302)]如果你把 ?secure?設(shè)置為 ?True?,則客戶端將模擬 HTTPS 請求。
post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?POST?請求,并返回一個 ?Response?對象,如下所述。
?data?字典中的鍵值對用于提交 ?POST?數(shù)據(jù)。例如:
>>> c = Client()
>>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})這將產(chǎn)生對這個 URL 的 ?POST?請求:
/login/且具有此 ?POST?數(shù)據(jù):
name=fred&passwd=secret如果你提供 ?application/json? 為 ?content_type?,則如果 ?data?是一個字典、列表或元組時,使用 ?json.dumps()? 進(jìn)行序列化。序列化默認(rèn)是通過 ?DjangoJSONEncoder?,可以通過為 Client 提供 ?json_encoder?參數(shù)覆蓋。這個序列化也會發(fā)生在 ?put()?、?patch()? 和 ?delete()? 請求中。
如果你要提供任何其他的 ?content_type?(例如 ?text/xml? 用于 XML 有效載荷),使用HTTP ?Content-Type? 頭中的 ?content_type?,?data?的內(nèi)容在 ?POST?請求中按原樣發(fā)送。
如果你沒有為 ?content_type?提供一個值,data 中的值將以 ?multipart/form-data? 的內(nèi)容類型進(jìn)行傳輸。在這種情況下,data 中的鍵值對將被編碼為多部分消息,并用于創(chuàng)建 ?POST?數(shù)據(jù)有效載荷。
要為一個給定的鍵提交多個值——例如,要指定 ?? 的選擇——為所需鍵提供一個列表或元組的值。例如,這個 ?data?的值將為名為 ?choices?的字段提交三個選擇值:
{'choices': ('a', 'b', 'd')}提交文件是一種特殊情況。要 ?POST?一個文件,你只需要提供文件字段名作為鍵,以及你想上傳的文件的文件句柄作為值。例如:
>>> c = Client()
>>> with open('wishlist.doc', 'rb') as fp:
... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})這里的?attachment?可以修改成你處理代碼時想要的名稱
你也可以提供任何類似于文件的對象(例如 ?StringIO?或 ?BytesIO?)作為文件句柄。如果你要上傳到 ?ImageField?,這個對象需要一個可以通過 ?validate_image_file_extension?驗證器的 ?name?屬性。例如:
>>> from io import BytesIO
>>> img = BytesIO(b'mybinarydata')
>>> img.name = 'myimage.jpg'請注意,如果你想在多次調(diào)用 ?post()? 時使用同一個文件句柄,那么你需要在兩次調(diào)用之間手動重置文件指針。最簡單的方法是在向 ?post()? 提供文件后手動關(guān)閉文件,如上所示。
你還應(yīng)確保文件的打開方式允許數(shù)據(jù)被讀取。如果你的文件包含二進(jìn)制數(shù)據(jù),如圖像,這意味著你需要以 ?rb? (讀取二進(jìn)制)模式打開文件。
?extra?參數(shù)的作用與 ?Client.get()? 相同。
如果你用 ?POST?請求的 URL 包含編碼參數(shù),這些參數(shù)將在 ?request.GET? 數(shù)據(jù)中提供。例如,如果你要請求:
>>> c.post('/login/?visitor=true', {'name': 'fred', 'passwd': 'secret'})處理這個請求的視圖可以詢問 ?request.POST? 來檢索用戶名和密碼,也可以詢問 ?request.GET?來確定該用戶是否是訪客。
如果將 ?follow?設(shè)置為 ?True?,客戶端將遵循所有重定向,并且將在響應(yīng)對象中設(shè)置 ?redirect_chain?屬性,該屬性是包含中間 URL 和狀態(tài)碼的元組。
如果你把 ?secure?設(shè)置為 ?True?,則客戶端將模擬 HTTPS 請求。
head(path, data=None, follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?HEAD?請求,并返回一個 ?Response?對象。這個方法的工作原理和 ?Client.get()? 一樣,包括 ?follow?、?secure?和 ?extra?參數(shù),只是它不返回消息主體。
options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?OPTIONS?請求并返回一個 ?Response?對象。用于測試 RESTful 接口。
當(dāng)提供 ?data?時,它將被用作請求主體并且 ?Content-Type? 頭被設(shè)置為 ?content_type?。
?follow?、 ?secure?和 ?extra?參數(shù)的作用與 ?Client.get()? 相同。
put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?PUT?請求,并返回一個 ?Response?對象。用于測試 RESTful 接口。
當(dāng)提供 ?data?時,它將被用作請求主體并且 ?Content-Type? 頭被設(shè)置為 ?content_type?。
?follow?、 ?secure?和 ?extra?參數(shù)的作用與 ?Client.get()? 相同。
patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?PATCH?請求,并返回一個 ?Response?對象。用于測試 RESTful 接口。
?follow?、 ?secure?和 ?extra?參數(shù)的作用與 ?Client.get()? 相同。
delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?DELETE?請求,并返回一個 ?Response?對象。用于測試 RESTful 接口。
當(dāng)提供 ?data?時,它將被用作請求主體并且 ?Content-Type? 頭被設(shè)置為 ?content_type?。
?follow?、 ?secure?和 ?extra?參數(shù)的作用與 ?Client.get()?相同。
trace(path, follow=False, secure=False, **extra)
在提供的 ?path?上發(fā)出一個 ?TRACE?請求,并返回一個 ?Response?對象。用于模擬診斷探針。
與其他請求方法不同,為了符合 RFC 7231#section-4.3.8 的要求,不提供 ?data?作為關(guān)鍵字參數(shù),該 RFC 要求跟蹤請求不能有主體。
?follow?、 ?secure?和 ?extra?參數(shù)的作用與 ?Client.get()? 相同。
login(**credentials)
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),并且你需要處理登錄用戶的問題,你可以使用測試客戶端的 ?login()? 方法來模擬用戶登錄網(wǎng)站的效果。
調(diào)用此方法后,測試客戶端將擁有通過任何可能構(gòu)成視圖一部分的基于登錄的測試所需的所有 cookie 和會話數(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)證后端,這個方法可能需要不同的憑證。它需要你的后端 ?authenticate()? 方法所需要的任何憑證。
如果憑證被接受且登錄成功,則 ?login()? 返回 ?True?。
最后,在使用這個方法之前,你需要記得創(chuàng)建用戶賬戶。正如我們上面所解釋的,測試運(yùn)行器是使用測試數(shù)據(jù)庫執(zhí)行的,默認(rèn)情況下,數(shù)據(jù)庫中不包含用戶。因此,在生產(chǎn)站點(diǎn)上有效的用戶賬戶在測試條件下將無法工作。你需要創(chuàng)建用戶作為測試套件的一部分--無論是手動創(chuàng)建(使用 Django 模型 API)還是使用測試夾具。記住,如果你想讓你的測試用戶有一個密碼,你不能直接通過設(shè)置密碼屬性來設(shè)置用戶的密碼——你必須使用 ?set_password()? 函數(shù)來存儲一個正確的哈希密碼?;蛘?,你可以使用 ?create_user()? 輔助方法來創(chuàng)建一個具有正確哈希密碼的新用戶。
force_login(user, backend=None)
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),你可以使用 ?force_login()? 方法來模擬用戶登錄網(wǎng)站的效果。當(dāng)測試需要用戶登錄,而用戶如何登錄的細(xì)節(jié)并不重要時,可以使用這個方法代替 ?login()?。
與 ?login()? 不同的是,這個方法跳過了認(rèn)證和驗證步驟:不活躍的用戶(?is_active=False?)被允許登錄,并且不需要提供用戶憑證。
用戶的 ?backend?屬性將被設(shè)置為 ?backend?參數(shù)的值(應(yīng)該是一個點(diǎn)分隔 Python 路徑字符串),如果沒有提供值,則設(shè)置為 ?settings.AUTHENTICATION_BACKENDS[0]??login()?調(diào)用的 ?authenticate()? 函數(shù)通常會對用戶進(jìn)行注釋。
這個方法比? login()? 快,因為它繞過了昂貴的密碼散列算法。另外,你也可以通過 在測試時使用較弱的哈希算法 來加快 login() 速度。
logout()
如果你的網(wǎng)站使用了 Django 的 認(rèn)證系統(tǒng),?logout()? 方法可以用來模擬用戶注銷網(wǎng)站的效果。
調(diào)用此方法后,測試客戶端的所有 cookie 和會話數(shù)據(jù)都會被清除為默認(rèn)值。隨后的請求將看起來來自一個 ?AnonymousUser?。
測試響應(yīng)
?get()?和 ?post()? 方法都會返回一個 ?Response?對象,這個 ?Response?對象與 Django 視圖返回的 ?HttpResponse?對象是 不 一樣的;測試響應(yīng)對象有一些額外的數(shù)據(jù),對測試代碼驗證很有用。
具體來說,?Response?對象具有以下屬性:
class Response
?client?:用于發(fā)出請求并得到響應(yīng)的測試客戶端。
?content?:以字節(jié)字符串形式的響應(yīng)主體。 這是視圖或任何錯誤消息所呈現(xiàn)的最終頁面內(nèi)容。
?context?:模板 ?Context?實例,用于渲染產(chǎn)生響應(yīng)內(nèi)容的模板。如果渲染的頁面使用了多個模板,那么 ?context?將是一個按渲染順序排列的 ?Context?對象列表。無論在渲染過程中使用了多少模板,你都可以使用 ?[]? 操作符來檢索上下文值。例如,上下文變量 ?name?可以使用:
>>> response = client.get('/foo/')
>>> response.context['name']
'Arthur'?exc_info?:一個由三個值組成的元組,它提供了關(guān)于在視圖期間發(fā)生的未處理異常(如果有)的信息。值是(type,value,traceback),與 Python 的 ?sys.exc_info()? 返回的值相同。它們的含義是:
- ?
type?:異常的類型。 - ?
value?:異常的實例。 - ?
traceback?:一個追溯對象,在最初發(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)時將會出現(xiàn)一個 ?ValueError?。
?request?:激發(fā)響應(yīng)的請求數(shù)據(jù)。
?wsgi_request?:由生成響應(yīng)的測試處理程序生成的 ?WSGIRequest?實例。
?status_code?:整數(shù)形式的響應(yīng) HTTP 狀態(tài)。
?templates?:用于渲染最終內(nèi)容的 ?Template?實例列表,按渲染順序排列。對于列表中的每個模板,如果模板是從文件中加載的,則使用 ?template.name? 獲得模板的文件名。(名字是一個字符串,如 ?admin/index.html?。)
?resolver_match?:響應(yīng)的 ?ResolverMatch?的實例。你可以使用 ?func?屬性,例如,驗證服務(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,訪問這個屬性會引發(fā)一個 ?Resolver404?異常。
和普通的響應(yīng)一樣,你也可以通過 ?HttpResponse.headers? 訪問頭信息。例如,你可以使用 ?response.headers['Content-Type']? 來確定一個響應(yīng)的內(nèi)容類型。
例外
如果你把測試客戶端指向一個會引發(fā)異常的視圖,并且 ?Client.raise_request_exception? 是 ?True?,那么這個異常將在測試用例中可見。然后你可以使用標(biāo)準(zhǔn)的 ?try ... except? 塊或 ?assertRaises()? 來測試異常。
測試客戶端看不到的異常只有 ?Http404?、?PermissionDenied?、?SystemExit?和 ?SuspiciousOperation?。Django 在內(nèi)部捕獲這些異常,并將其轉(zhuǎn)換為相應(yīng)的 HTTP 響應(yīng)代碼。在這些情況下,你可以在測試中檢查 ?response.status_code?。
如果 ?Client.raise_request_exception? 為 ?False?,測試客戶端將返回一個 500 的響應(yīng),就像返回給瀏覽器一樣。響應(yīng)有屬性 ?exc_info?來提供關(guān)于未處理的異常的信息。
持久狀態(tài)
測試客戶端是有狀態(tài)的。如果一個響應(yīng)返回一個 cookie,那么這個 cookie 將被存儲在測試客戶端,并與所有后續(xù)的 ?get()? 和 ?post()? 請求一起發(fā)送。
不遵循這些 cookie 的過期策略。如果你希望 cookie 過期,請手動刪除它或創(chuàng)建一個新的 Client 實例(這將有效地刪除所有 cookie)。
測試客戶端有兩個屬性,存儲持久化的狀態(tài)信息。你可以作為測試條件的一部分來訪問這些屬性。
?Client.cookies?:一個 Python ?SimpleCookie?對象,包含所有客戶端 cookie 的當(dāng)前值。
?Client.session?:一個類似字典的對象,包含會話信息。要修改會話然后保存,必須先將其存儲在一個變量中(因為每次訪問該屬性時都會創(chuàng)建一個新的 ?SessionStore?):
def test_something(self):
session = self.client.session
session['somekey'] = 'test'
session.save()設(shè)置語言
在測試支持國際化和本地化的應(yīng)用程序時,你可能想為測試客戶端請求設(shè)置語言。這樣做的方法取決于 ?LocaleMiddleware?是否啟用。
如果啟用了中間件,可以通過創(chuàng)建一個名為 ?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.")或在請求中加入 ?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è)置活動語言:
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.")以下是使用測試客戶端進(jìn)行的單元測試:
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) 當(dāng)前題目:創(chuàng)新互聯(lián)Django4.0教程:Django4.0 測試工具-測試客戶端
標(biāo)題URL:http://www.fisionsoft.com.cn/article/dpsppgj.html


咨詢
建站咨詢
