Django 测试速查表

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

Django应用中通用测试模式和最佳实践速查表.


001.png

免责声明: 这里所提供的示例不一定是在Django中执行X的“正确方法”。请联系我提出你的更改或增加代码建议。


要继续学习本文,请一定要创建一个新的Django项目。在项目就绪后,创建一个名为library的Django应用程序:


001.jpg


接下来,在 settings.py中启用该应用程序:


002.jpg

序曲:我怎么知道要测试什么?

使用coverage。在你的Django项目中安装该包:


003.jpg


在项目文件夹中运行该工具:


001.jpg


在第一次运行成功后,你可以使用以下命令来获取一个覆盖范围:


002.jpg


你还可以使用以下命令生成一个HTML报告(项目根目录中会出现一个名为htmlcov的文件夹):


003.jpg


测试组织结构

测试组织结构是比较困难的,并且在很大程度上依赖于团队的偏好。一个好的起点是你至少要对你的测试文件进行分割。


你可以创建一个tests文件夹,并让其中的每个文件分别保存应用程序的单个部分的测试,而不是在应用程序文件夹中只有一个单一的tests.py:


001.jpg


在这里你可以看到一个带有api.py和web.py的tests文件夹。api.py保存了API端点的测试,而web.py可以测试常规的HTML页面。你也可以创建一个models.py用来保存测试模型。


另一种常见的方法是创建一个feature文件夹,其中的每个文件用来保存针对单个应用程序功能的测试:


001.jpg


在本指南中,我们将使用第一种方法。


测试一个多对多关系

场景:测试两个相关的模型。


假设有两个模型:Book和Author。一本书可以有许多作者,而一个作者也可以有许多相互关联的书。要表达这种关系,我们可以从Book到Author应用一个ManyToManyField。


如果你想要继续学习,请在library/models.py中创建这两个模型: 


001.jpg


然后运行并应用该迁移:


002.jpg


作为第一个测试,我们可能想要检查我们没有忘记添加ManyToManyField。给定一本Book,我们想要计数一个或多个Author。


我们可以在一个名为library/tests/models.py的文件中创建以下测试:


001.jpg


这里我们创建了一个book和两个author。要将我们的author分配给该book,我们可以这样做:


001.jpg


我们也可以执行相反的操作,将该book分配给每一个author:


001.jpg


要运行该测试,请在library/tests/__init__.py中导入 library/tests/models.py:


001.jpg


然后运行:


002.jpg


资源:


  • 多对多关系

  • Django测试用例


测试模型字符串

场景:测试一个模型字符串表示形式


Django模型可能有一个__str__方法来决定如何将模型以一个字符串进行表示。再次考虑一下我们的模型:


001.jpg


要将Author显示为first_name + last_name并且用它的标题来显示Book,我们可以向每个模型添加相应的__str__方法:


001.jpg


我们可以在名为library/tests/models.py 的文件中添加以下测试:


001.jpg


要运行该测试,请在 library/tests/__init__.py中导入library/tests/models.py:


001.jpg


然后运行:


002.jpg


批量测试模型字段

场景:测试“拥挤的”模型


假设有一个带有多个字段的拥挤的Django模型:


001.jpg


在测试中手动填充每个字段将是不现实的:


001.jpg


相反,使用像Model bakery这样的工具,你可以委托字段创建。使用以下命令来安装Model bakery:


001.jpg


然后在你的测试中创建以下内容:


001.jpg


如果需要重写字段,你可以传递自己的字段。Model bakery也可以很方便地生成大量的模型。请看下面Jeff的帖子。


资源:


  • Model bakery

  • 我要如何在Django和DRF中生成1000个对象来进行测试?


测试POST请求

场景:使用HTML表单接受“/contacts/”上的POST请求。


假设你想使用Django为你的library应用程序创建一个联系人表单,以便从学生那里获得联系人。首先,你可能想要为它编写一个测试。


按照我们创建的测试结构,在library/tests/web.py中创建一个新文件。在这个文件中,我们可以导入我们的模型TestCase,并为该测试编写一个框架:


001.jpg


现在,为了测试这个特性,我们需要创建一些与POST请求一起发送的数据。注意,这些数据应该与模型的字段相匹配。


因此,给定这样一个假设的模型:


002.jpg


在这个测试中我们可以编写一个数据字典:


001.jpg


现在我们使用Django测试客户端来发送请求。作为第一个测试,我们可以检查是否在数据库中创建了一个Contact实例:


002.jpg


接着我们可以检查视图是否正确地进行了重定向:


001.jpg


这是一个针对web开发中常见的经典POST/Redirect/GET模式的测试。


请注意,由于上面的测试完全跳过了HTML表单。你可能还想测试模板(或者至少几个HTML输入):


001.jpg


在这里,我们测试了HTML中的两个模型字段的外观:


001.jpg


由于你在HTML中包含了表单,因此测试几个字段就足够了。另一件需要记住的事情是,测试客户端跳过了CSRF验证。不要忘记在模板中包含令牌!对于更实际的测试,你还可以使用Selenium或Splinter。


要运行该测试,请在 library/tests/__init__.py中导入library/tests/web.py:


002.jpg


然后运行:


003.jpg


要使这个测试通过,你可以按照这里所描述的使用一个Django CreateView。


资源:


  • POST/Redirect/GET 模式

  • Django测试客户端


在测试中使用更健壮的URL

在测试中这样调用URL也是可以的:


001.jpg


要更好一些的话,你可以使用reverse来避免脆弱的测试:


002.jpg


现在你就可以通过名称而不是路径来引用URL,只要你在相应的urls.py中命名你的URL即可。例如,一个假想的library/urls.py看起来将是这样的:


001.jpg


查看本文获取完整的示例。


从Django模型提供数据字典

在某些情况下,你希望使用POST请求测试同一个块中的模型实例。为了避免重复,你可以在这个模型实例上使用model_to_dict:


001.jpg


感谢Augusto提供的这个建议。


测试验证

场景:仅向经过身份验证的用户显示“/download/”页面。


我们有一个连接到一个视图的URL“/download/”。只有经过身份验证的用户才能访问这个视图。作为第一个测试,我们可以检查任何匿名用户是否被重定向到settings.LOGIN_URL(默认为/accounts/login/后跟?next=/download/)

中定义的登录页面:


未标题-1_conew1.bmp


相反,经过身份验证的用户可以访问该页面。为了测试一个通过验证的用户,我们在测试块中创建该用户,并且使用client.force_login()让它通过:


001.jpg


注意,你应该将from django.contrib.auth.models import User与任何自定义的Django用户进行交换,如果存在的话。


资源:


  • 限制对已登录用户的访问


测试请求头部

场景:我们想要根据一个请求头部来测试Django行为。


这个场景对于测试Django中间件或者任何根据请求头部做出决定的视图来说非常有用。请考虑以下视图:


001.jpg


它会根据HTTP_HOST的值返回两个不同的响应。一个更健壮的带有get_host的版本:


002.jpg


当没有指定HTTP_HOST时,该视图的第一个测试会检查响应是否包含“错误的主机!”:


003.jpg


通过另一个测试,如果HTTP_HOST不是“www.my-domain.dev”时,我们可以来检查“错误的主机!”。传递HTTP_HOST有两种方法。选项一:


001.jpg


这里client.get()会接受额外的关键字参数。选项二:


002.jpg


这里,我们用一个自定义头部来构造客户端。这两个选项都是有效的。为了了解一下上下文,以下是三个测试:


001.jpg


最后,我们可以通过将预期的HTTP_HOST传入另一个测试来测试“正确的主机!”(同样,选择你自己的风格来传入头部):


 001.jpg


完整的测试套件:


001.jpg


这个测试的一个常见用例是一个为多个域名提供请求服务的Django项目,其中每个域必须加载有且只有一个Django应用程序。


资源:


  • HttpRequest META

  • 如何在Django中处理多个站点(虚拟主机)


Django REST框架插曲

Django REST框架(从现在开始简称为DRF)是一个用于构建RESTful API的非常棒的Django工具。要在你的项目中安装Django REST框架,请运行:


001.jpg


接着,在settings.py中启用DRF:


002.jpg


DRF在Django的TestCase或LiveServerTestCase上提供了一组自定义的测试类。APITestCase是测试DRF端点的首选类。


DRF: 测试POST请求

场景:在"api/contacts/"上接受API端点上的POST请求。


要测试你的API,你可以在library/tests/api.p中创建一个新文件,其中带有针对该测试的一个骨架:


001.jpg


要测试这个特性,我们需要创建一些数据来与POST请求一起发送。注意这些数据要与模型的字段相匹配。


要使这个测试在DRF中通过,你需要:


  • 一个模型

  • 一个模型序列化器

  • 一个 CreateAPIView 和相应的URL


这里列出了有关使用Django REST框架的指南。


给定这样一个假设的模型:


001.jpg


我们可以像这样进行测试:


002.jpg


在这种简单的情况下没有多少东西需要去测试,但是如果你像我一样偏执,进行201检查也没有坏处:


001.jpg


要运行该测试,请在library/tests/__init__.py中导入library/tests/api.py (以及你之前写的任何测试)  :


001.jpg


然后,只运行这个API测试:


002.jpg


DRF: 测试验证

场景:在"api/secret/"上仅接受通过身份验证的用户在API端点上发起的GET请求。


我们将一个端点“api/secret/”连接到了一个DRF ListView。只有经过身份验证的用户才能访问此视图。作为第一个测试,我们可以检查任何匿名用户会得到一个403禁止访问错误:


001.jpg


一个使该测试通过的最小视图可以是这样的:


001.jpg


这个视图会通过在一个假设的JavaScript前端的相同上下文中调用API来展现会话验证。在一个解耦的架构中,你可以使用基于令牌的身份验证。


相反,经过身份验证的用户可以访问该页面。为了测试一个经过身份验证的用户,我们可以在测试块中创建该用户,并且使用client.force_login()让它通过测试:


001.jpg


注意,你应该将from django.contrib.auth.models import User与任何自定义的Django用户进行交换,如果存在的话。


资源:


  • 在DRF上设置认证方案


更多

更多教程即将推出。请持续关注!

英文原文:https://www.valentinog.com/blog/testing-django/
译者:忧郁的红秋裤