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'...
posted @ 2016-08-29 19:38  Python黑板报  阅读(1808)  评论(10编辑  收藏  举报