Django 中的 csrf_token 與單元測試報錯處理
阿新 • • 發佈:2019-02-04
在Harry J.W. Percival 所著的<Python Web開發:測試驅動方法>中的第五章,在單元測試部分存在一個bug。
即,在高版本的Django(>1.7)中,在渲染模板時,Django 會把這個模板標籤替換成一個<input
type="hidden">
元素,其值是CSRF 令牌。
所以在執行單元測試時,通過檢視函式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)
home_page()
渲染得到的響應包含csrf轉換的<input>
元素,而render_to_string()
則未生成該部分,所以導致測試失敗。
會產生如下錯誤訊息。
在部落格http://www.cnblogs.com/panzeyan/p/5819373.html中,給出了1.8.x~1.9.x的解決方案,即在呼叫$ 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'...
render_to_string()
時的末尾新增request=request
但這種解決方案沒有辦法在筆者的1.10.x版本正常使用。因為檢視中產生的令牌值與呼叫render_to_string()返回的令牌值不同。
在經過多次查詢後可知,這個問題暫時沒有辦法以正規途徑解決,甚至原文作者也建議大家使用1.8.7版本,因為1.8.x是長期支援版本。
https://groups.google.com/forum/#!topic/obey-the-testing-goat-book/fwY7ifEWKMU
但為了不影響自己的使用並且不降級django的版本,筆者決定採用討巧的方法,利用正則表示式刪除相關的標籤。程式碼如下:
csrf_regex = r'<input[^>]+csrfmiddlewaretoken[^>]+>'
print('expected_html\n',expected_html)
observed_html = re.sub(csrf_regex, '', response.content.decode())
expected_html = re.sub(csrf_regex, '', expected_html)
雖然並不建議大家使用這種方法,但在特殊情況下也可以參考一下。