作者:HelloGitHub-追梦人物
文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库
我们博客功能越来越来完善了,但这也带来了一个问题,我们不敢轻易地修改已有功能的代码了!
我们怎么知道代码修改后带来了预期的效果?万一改错了,不仅新功能没有用,原来已有的功能都可能被破坏。此前我们开发一个新的功能,都是手工运行开发服务器去验证,不仅费时,而且极有可能验证不充分。
如何不用每次开发了新的功能或者修改了已有代码都得去人工验证呢?解决方案就是编写自动化测试,将人工验证的逻辑编写成脚本,每次新增或修改代码后运行一遍测试脚本,脚本自动帮我们完成全部测试工作。
接下来我们将进行两种类型的测试,一种是单元测试,一种是集成测试。
单元测试是一种比较底层的测试,它将一个功能逻辑的代码块视为一个单元(例如一个函数、方法、或者一个 if 语句块等,单元应该尽可能小,这样测试就会更加充分),程序员编写测试代码去测试这个单元,确保这个单元的逻辑代码按照预期的方式执行了。通常来说我们一般将一个函数或者方法视为一个单元,对其进行测试。
集成测试则是一种更加高层的测试,它站在系统角度,测试由各个已经经过充分的单元测试的模块组成的系统,其功能是否符合预期。
我们首先来进行单元测试,确保各个单元的逻辑都没问题后,然后进行集成测试,测试整个博客系统的可用性。
Python 一般使用标准库 unittest 提供单元测试,django 拓展了单元测试,提供了一系列类,用于不同的测试场合。其中最常用到的就是 django.test.TestCase 类,这个类和 Python 标准库的 unittest.TestCase 类似,只是拓展了以下功能:
提供了一个 client 属性,这个 client 是 Client 的实例。可以把 Client 看做一个发起 HTTP 请求的功能库(类似于 requests),这样我们可以方便地使用这个类测试视图函数。
运行测试前自动创建数据库,测试运行完毕后自动销毁数据库。我们肯定不希望自动生成的测试数据影响到真实的数据。
博客应用的单元测试,主要就是和这个类打交道。
django 应用的单元测试包括:
测试 model,model 的方法是否返回了预期的数据,对数据库的操作是否正确。
测试表单,数据验证逻辑是否符合预期
测试视图,针对特定类型的请求,是否返回了预期的响应
其它的一些辅助方法或者类等
接下来我们就逐一地来测试上述内容。
搭建测试环境测试写在 tests.py 里(应用创建时就会自动创建这个文件),首先来个冒烟测试,用于验证测试功能是否正常,在 blog\tests.py 文件写入如下代码:
from django.test import TestCase class SmokeTestCase(TestCase): def test_smoke(self): self.assertEqual(1 + 1, 2)使用 manage.py 的 test 命令将自动发现 django 应用下的 tests 文件或者模块,并且自动执行以 test_ 开头的方法。运行:pipenv run python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
-------------------------------------------------------
Ran 1 test in 0.002s
OK
Destroying test database for alias 'default'...
OK 表明我们的测试运行成功。
不过,如果需要测试的代码比较多,把全部测试逻辑一股脑塞入 tests.py,这个模块就会变得十分臃肿,不利于维护,所以我们把 tests.py 文件升级为一个包,不同的单元测试写到包下对应的模块中,这样便于模块化地维护和管理。
删除 blog\tests.py 文件,然后在 blog 应用下创建一个 tests 包,再创建各个单元测试模块:
blog\ tests\ __init__.py test_smoke.py test_models.py test_views.py test_templatetags.py test_utils.pytest_models.py 存放和模型有关的单元测试
test_views.py 测试视图函数
test_templatetags.py 测试自定义的模板标签
test_utils.py 测试一些辅助方法和类等
注意
tests 包中的各个模块必须以 test_ 开头,否则 django 无法发现这些测试文件的存在,从而不会运行里面的测试用例。
测试模型模型需要测试的不多,因为基本上都是使用了 django 基类 models.Model 的特性,自己的逻辑很少。拿最为复杂的 Post 模型举例,它包括的逻辑功能主要有:
__str__ 方法返回 title 用于模型实例的字符表示
save 方法中设置文章创建时间(created_time)和摘要(exerpt)
get_absolute_url 返回文章详情视图对应的 url 路径
increase_views 将 views 字段的值 +1