Django 中的 csrf_token 與單元測試


Django 中的 csrf_token 與單元測試

《Python Web開發:測試驅動方法》一書中作者使用的 Django 版本是 1.7,而我使用的是1.9.7版(官網已經更新到1.10了)。這就導致書中給出的代碼可能有“過時”的部分。

比如,下面是第三章一個單元測試tests.py的代碼,運行沒有問題。但是當第五章引入表單后,相應模板中需在<form>標簽內加入CSRF令牌{% csrf_token %}。此時再次運行此單元測試會報錯。

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)

錯誤信息:

$ 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'...

根據錯誤信息,是最后一行的斷言self.assertEqual(response.content.decode(), expected_html)導致測試失敗。

由於AssertionError信息顯示不完整,所以將該行斷言注釋掉,添加2行代碼,打印出response.content.decode()expected_html的全部內容。

print('response.content.decode()\n', response.content.decode())
print('expected_html\n', expected_html)

運行測試:

$ 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'...

在渲染模板時,Django 會把這個模板標簽替換成一個<input type="hidden">元素,其值是CSRF 令牌。
從上面的html代碼可以看出,通過視圖函數home_page()渲染得到的響應包含csrf轉換的<input>元素,而render_to_string()則未生成該部分,所以導致測試失敗。

以 “django csrf_token 測試”為關鍵字google下,發現已經有人碰到這個問題。可惜代碼不全,網頁中提到的參考鏈接已經失效。

再以失效鏈接中的“django-csrf_token-when-using-render_to_string”為關鍵詞google,stackoverflow上有人給出一個簡單的解決方法,在tests.py中的render_to_string()函數內中加一個參數

expected_html = render_to_string('home.html', request=request)

運行測試,不再報錯,問題解決。

$ python3 manage.py test lists

Creating test database for alias 'default'...

..

----------------------------------------------------------------------

Ran 2 tests in 0.012s



OK

Destroying test database for alias 'default'...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM