美文网首页
Django 中的 csrf_token 与单元测试

Django 中的 csrf_token 与单元测试

作者: 潘泽彦 | 来源:发表于2016-08-29 19:35 被阅读291次

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

    相关文章

      网友评论

          本文标题:Django 中的 csrf_token 与单元测试

          本文链接:https://www.haomeiwen.com/subject/xaxfettx.html