【有书共读15】Python测试驱动开发 读书笔记 10
保存用户输入:测试数据库
若想让浏览器发送 POST 请求,要给 <input> 元素指定 name= 属性,然后把它放在 <form> 标签中,并为 <form> 标签指定 method="POST" 属性,这样浏览器才能向服务器发送 POST 请求。调整一下 lists/templates/home.html 中的模板:
<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">现在运行功能测试,会看到一个晦涩难懂、预料之外的错误:
[...]
Traceback (most recent call last):
File "functional_tests.py", line 39, in test_can_start_a_list_and_retrieve_it_later
table = self.browser.find_element_by_id('id_list_table') [...]
selenium.common.exceptions.NoSuchElementException: Message: 'Unable to locate element: {"method":"id","selector":"id_list_table"}' ; Stacktrace [...]
如果功能测试出乎意料地失败了,可以做下面几件事,找出问题所在:
• 添加 print 语句,输出页面中当前显示的文本是什么;
• 改进错误消息,显示当前状态的更多信息;
• 亲自手动访问网站;
• 在测试执行过程中使用 time.sleep 暂停。
本书会分别介绍这几种调试方法,不过我发现自己经常使用 time.sleep。下面试一下这种方法。在错误发生位置的前面加上 time.sleep:
# 按回车键后,页面更新了
# 待办事项表格中显示了"1: Buy peacock feathers" inputbox.send_keys(Keys.ENTER)
import time time.sleep(10)
table = self.browser.find_element_by_id('id_list_table')
如果 Selenium 运行得很慢,你可能已经发现了这一问题。现在再次运行功能测试,就有机会看看到底发生了什么:你会看到一个如图 5-1 所示的页面,显示了 Django 提供的很多调试信息。
SHAPE \* MERGEFORMAT
Django 针对 CSRF 的保护措施是在生成的每个表单中放置一个自动生成的令牌,通过这个令牌判断 POST 请求是否来自同一个网站。
之前的模板都是纯粹的HTML,在这里要首次体验Django 模板的魔力,使用“模板标签”
(template tag)添加CSRF 令牌。模板标签的句法是花括号和百分号形式,即{% ... %}—— 这种写法很有名,要连续多次同时按两个键,是世界上最麻烦的输入方式。
<form method="POST">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item" />
{% csrf_token %}
</form>
渲染模板时,Django 会把这个模板标签替换成一个 <input type="hidden"> 元素,其值是
CSRF 令牌。现在运行功能测试,会看到一个预期失败:
AssertionError: False is not true : New to-do item did not appear in table
因为 time.sleep 还在,所以测试会在最后一屏上暂停。可以看到,提交表单后新添加的待办事项不见了,页面刷新后又显示了一个空表单。这是因为还没连接服务器让它处理
POST 请求,所以服务器忽略请求,直接显示常规首页。
其实,现在可以删掉 time.sleep 了:
functional_tests.py
# 待办事项表格中显示了“1: Buy peacock feathers” inputbox.send_keys(Keys.ENTER)
table = self.browser.find_element_by_id('id_list_table')