在《Python Web開(kāi)發(fā):測(cè)試驅(qū)動(dòng)方法》一書(shū)中作者使用的 Django 版本是 1.7,而我使用的是1.9.7版(官網(wǎng)已經(jīng)更新到1.10了)。這就導(dǎo)致書(shū)中給出的代碼可能有“過(guò)時(shí)”的部分佳鳖。
比如葱跋,下面是第三章一個(gè)單元測(cè)試tests.py
的代碼氨淌,運(yùn)行沒(méi)有問(wèn)題萤彩。但是當(dāng)?shù)谖逭乱氡韱魏螅鄳?yīng)模板中需在<form>
標(biāo)簽內(nèi)加入CSRF令牌{% csrf_token %}
坚弱。此時(shí)再次運(yùn)行此單元測(cè)試會(huì)報(bào)錯(cuò)蜀备。
from django.test import TestCase
from django.core.urlresolvers import resolve
from django.http import HttpRequest
from django.template.loader import render_to_string
from .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()
response = home_page(request)
expected_html = render_to_string('home.html')
self.assertEqual(response.content.decode(), expected_html)
錯(cuò)誤信息:
$ python3 manage.py test lists
Creating test database for alias 'default'...
F.
======================================================================
FAIL: test_home_page_returns_correct_html (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/panzeyan/PycharmProjects/TDD/superlists/lists/tests.py", line 20, in test_home_page_returns_correct_html
self.assertEqual(response.content.decode(), expected_html)
AssertionError: '<htm[240 chars] <input type=\'hidden\' name=\'csrfmiddlew[184 chars]l>\n' != '<htm[240 chars] \n </form>\n\n <table id="i[87 chars]l>\n'
----------------------------------------------------------------------
Ran 2 tests in 0.256s
FAILED (failures=1)
Destroying test database for alias 'default'...
根據(jù)錯(cuò)誤信息,是最后一行的斷言self.assertEqual(response.content.decode(), expected_html)
導(dǎo)致測(cè)試失敗荒叶。
由于AssertionError
信息顯示不完整碾阁,所以將該行斷言注釋掉,添加2行代碼些楣,打印出response.content.decode()
和 expected_html
的全部?jī)?nèi)容脂凶。
print('response.content.decode()\n', response.content.decode())
print('expected_html\n', expected_html)
運(yùn)行測(cè)試:
$ python3 manage.py test lists
Creating test database for alias 'default'...
response.content.decode()
<html>
<head>
<title>To-Do lists</title>
</head>
<body>
<h1>Your To-Do list</h1>
<form method="post">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item" />
<input type='hidden' name='csrfmiddlewaretoken' value='tl2rZy1RBSLY75DD2ysZ4KHF0DePGWQs' />
</form>
<table id="id_list_table">
<tr><td>1: </td></tr>
</table>
</body>
</html>
expected_html
<html>
<head>
<title>To-Do lists</title>
</head>
<body>
<h1>Your To-Do list</h1>
<form method="post">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item" />
</form>
<table id="id_list_table">
<tr><td>1: </td></tr>
</table>
</body>
</html>
..
----------------------------------------------------------------------
Ran 2 tests in 0.015s
OK
Destroying test database for alias 'default'...
在渲染模板時(shí),Django 會(huì)把這個(gè)模板標(biāo)簽替換成一個(gè)<input type="hidden">
元素,其值是CSRF 令牌愁茁。
從上面的html代碼可以看出蚕钦,通過(guò)視圖函數(shù)home_page()
渲染得到的響應(yīng)包含csrf轉(zhuǎn)換的<input>
元素,而render_to_string()
則未生成該部分鹅很,所以導(dǎo)致測(cè)試失敗嘶居。
以 “django csrf_token 測(cè)試”為關(guān)鍵字google下,發(fā)現(xiàn)已經(jīng)有人碰到這個(gè)問(wèn)題促煮∮势ǎ可惜代碼不全整袁,網(wǎng)頁(yè)中提到的參考鏈接已經(jīng)失效。
再以失效鏈接中的“django-csrf_token-when-using-render_to_string”為關(guān)鍵詞google佑吝,stackoverflow上有人給出一個(gè)簡(jiǎn)單的解決方法坐昙,在tests.py
中的render_to_string()
函數(shù)內(nèi)中加一個(gè)參數(shù)
expected_html = render_to_string('home.html', request=request)
運(yùn)行測(cè)試,不再報(bào)錯(cuò)迹蛤,問(wèn)題解決民珍。
$ python3 manage.py test lists
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.012s
OK
Destroying test database for alias 'default'...