编写并运行表单的测试
了解网页表单以及如何编写和测试它们。
在当今的Web应用中,网页表单非常普遍。表单用于收集应用程序用户的数据(输入)。因此,它使网站更具动态性,因为我们能够利用收集到的信息来改变网页内容。
例如,像Google这样的搜索引擎通过网页表单接收用户的输入,并根据这些信息定制搜索结果。此外,所有要求我们登录的网站,如GitHub,都需要我们提供用户名和密码。这些信息用于判断我们是否为合法用户。
另外,如果我们想在线购买商品,比如在亚马逊网站上购物,系统会要求我们输入信用卡信息。
由于本项目需要允许用户将书籍保存到书目中,因此我们需要一个网页表单来收集这些信息,然后将其保存到数据库中,以便日后归还给用户。
网页表单示例
以下是一个用HTML编写的表单示例:
1<form method="" action=""> 2 <input type="text" name="name"> 3 <input type="password" name="password"> 4 <input type="submit" value="提交"> 5</form>
在上述代码中,所有输入字段都包裹在 <form> 标签内。<form> 标签还包含两个我们将要讨论的属性:method 和 action。method 属性定义了表单数据的提交方式。通常,网页表单支持两种方法——GET 和 POST。
两者的区别在于:GET 方法将数据作为URL参数传递,不适用于敏感数据;而 POST 方法则将数据放在请求体中。
此外,大多数对数据库进行修改或生成新数据的操作应使用 POST 请求。正如其名称所示,这是上传数据的方式。而 GET 方法表示我们仅获取数据,不进行任何修改。
每个输入字段都应添加 name 属性。这在Django后端中至关重要,用于定位特定字段。当表单提交时,它会被转换为键值对形式,其中 name 是键,用户输入的实际值是值。
我们的表单还包含一个 action 属性。在这里,我们指定表单数据应发送到哪个URL进行处理。如果该属性为空,则数据将发送到当前页面。如果未设置 method 属性,则默认使用 GET 方法。
隐藏表单字段
在线表单中可以使用隐藏字段。这些字段在网页上不可见。这对Django非常重要,因为它利用这些字段来设置CSRF令牌。
什么是CSRF?
跨站请求伪造(CSRF)是指攻击者诱使用户向另一个网站提交表单。例如,假设我们访问某个网站,由于该网站上的某些操作,我们无意中向另一个网站提交了表单。
这会对安全造成严重后果。攻击者可以诱导我们无意识地修改我们常用网站上的任意内容。攻击者只需要了解目标网站的结构即可。
目前大多数Web框架(包括Django)都能防止此类攻击。Django使用CSRF令牌,这是一个为每位网站访客生成的唯一短随机字符串。因此,只有当表单以 POST 请求提交时,才必须提供此令牌以识别访问者身份。
注意:CSRF令牌还会定期变化。这使得攻击者更难利用可能已泄露的令牌。
为表单编写测试
我们继续推进本课程的项目,开始编写表单的测试。完整的测试代码如下:
No configuration found for ID: 7af8f6c96224f14d1cbe2e60dd392da513cbbd234b72c86955956197241cb84a
让我们看看 tests.py 文件中的新内容:
第7行:我们看到表单测试类 CatalogueFormTests。它继承自 SimpleTestCase 类,因为该测试不涉及数据库操作。
setUp 方法是该类中的第一个方法。它的作用是在访问首页时返回一个响应。请注意,我们目前只有一个页面,上面包含表单。
第13行:我们看到第一个测试方法 test_book_form。该方法用于测试用于向书目中添加书籍的完整表单。
第14行:我们首先获取表单本身,命名为 add_book_form。
获取后,将其保存在一个名为 form 的变量中,并验证模板中使用的是否为同一表单实例,此处即首页。
第16行:我们再次确认CSRF令牌已包含在内。需要注意的是,任何处理 POST 请求的表单都必须包含此令牌。
第18行:接下来的测试方法旨在确保表单样式正确。未来我们将使用Bootstrap作为默认样式。为此,我们检查表单输入字段中是否包含 form-control 类。
第22行:最后一个测试函数用于验证用户无法提交空表单数据,且应显示适当的错误提示信息。
执行测试
编写完测试后,现在运行测试以确认它们失败。因为TDD的第二步是执行刚刚编写的测试,而测试失败说明我们正在逐步添加新功能。请按以下步骤运行测试:
-
点击“运行”按钮。
-
在“终端”标签页中,按键盘上的 “Ctrl+C” 停止开发服务器。
-
使用以下命令分别执行每个测试方法:
python3 manage.py test elibrary_app.tests.CatalogueFormTests.test_book_formpython3 manage.py test elibrary_app.tests.CatalogueFormTests.test_bootstrap_class_used_for_default_stylingpython3 manage.py test elibrary_app.tests.CatalogueFormTests.test_book_form_validation_for_blank_items
正如预期,所有测试均失败,因为我们尚未创建表单。