测试覆盖率 — Flask 文档
测试覆盖率
为您的应用程序编写单元测试可以让您检查您编写的代码是否按您期望的方式工作。 Flask 提供了一个测试客户端来模拟对应用程序的请求并返回响应数据。
你应该尽可能多地测试你的代码。 函数中的代码只在函数被调用时运行,分支中的代码,如if
块,只在满足条件时运行。 您希望确保每个函数都使用覆盖每个分支的数据进行测试。
越接近 100% coverage,您就越能放心,做出更改不会意外地改变其他行为。 但是,100% coverage 并不能保证您的应用程序没有错误。 特别是,它不会测试用户如何与浏览器中的应用程序交互。 尽管如此,测试覆盖率是开发过程中使用的重要工具。
笔记
这将在本教程的后面介绍,但在您未来的项目中,您应该在开发时进行测试。
您将使用 pytest 和 coverage 来测试和衡量您的代码。 安装它们:
$ pip install pytest coverage
设置和夹具
测试代码位于tests
目录下。 这个目录在flaskr
包的旁边的,不在里面。 tests/conftest.py
文件包含每个测试将使用的名为 fixtures 的设置函数。 测试位于以 test_
开头的 Python 模块中,并且这些模块中的每个测试函数也以 test_
开头。
每个测试都会创建一个新的临时数据库文件并填充一些将在测试中使用的数据。 编写一个 SQL 文件来插入该数据。
tests/data.sql
app
夹具将调用工厂并通过 test_config
来配置应用程序和数据库进行测试,而不是使用您本地的开发配置。
tests/conftest.py
tempfile.mkstemp()
创建并打开一个临时文件,返回文件描述符和它的路径。 DATABASE
路径被覆盖,因此它指向这个临时路径而不是实例文件夹。 设置好路径后,创建数据库表,插入测试数据。 测试结束后,临时文件被关闭并删除。
TESTING 告诉 Flask 应用程序处于测试模式。 Flask 更改了一些内部行为,使其更易于测试,其他扩展也可以使用该标志使测试更容易。
client
夹具使用由 app
夹具创建的应用程序对象调用 app.test_client()
。 测试将使用客户端向应用程序发出请求,而无需运行服务器。
runner
夹具类似于 client
。 app.test_cli_runner()
创建一个运行器,可以调用在应用程序中注册的 Click 命令。
Pytest 通过将它们的函数名称与测试函数中的参数名称进行匹配来使用设备。 例如,您接下来要编写的 test_hello
函数接受一个 client
参数。 Pytest 将其与 client
夹具函数匹配,调用它,并将返回值传递给测试函数。
工厂
关于工厂本身没有太多可测试的。 大多数代码已经为每个测试执行了,所以如果某些东西失败了,其他测试会注意到。
唯一可以改变的行为是通过测试配置。 如果 config 没有通过,则应该有一些默认配置,否则应该覆盖该配置。
tests/test_factory.py
您在教程开始时编写工厂时添加了 hello
路由作为示例。 它返回“Hello, World!”,因此测试检查响应数据是否匹配。
数据库
在应用程序上下文中, get_db
每次调用时都应该返回相同的连接。 在上下文之后,应该关闭连接。
tests/test_db.py
init-db
命令应该调用 init_db
函数并输出一条消息。
tests/test_db.py
此测试使用 Pytest 的 monkeypatch
固定装置将 init_db
函数替换为记录它已被调用的函数。 您上面写的 runner
夹具用于按名称调用 init-db
命令。
认证
对于大多数视图,需要用户登录。 在测试中最简单的方法是向客户端的 login
视图发出 POST
请求。 与其每次都写出来,您可以编写一个带有方法的类来执行此操作,并使用固定装置将其传递给每次测试的客户端。
tests/conftest.py
使用auth
夹具,可以在测试中调用auth.login()
以test
用户登录,该用户作为测试数据的一部分插入[ X149X] 夹具。
register
视图应该在 GET
上成功渲染。 在具有有效表单数据的 POST
上,它应该重定向到登录 URL,并且用户的数据应该在数据库中。 无效数据应显示错误消息。
tests/test_auth.py
client.get()
发出 GET
请求并返回 Flask 返回的 Response
对象。 类似地,client.post()
发出 POST
请求,将 data
dict 转换为表单数据。
为了测试页面是否成功呈现,发出一个简单的请求并检查 200 OK
status_code
。 如果渲染失败,Flask 会返回一个 500 Internal Server Error
代码。
当注册视图重定向到登录视图时,headers
将有一个带有登录 URL 的 Location
标头。
data
包含作为字节的响应正文。 如果您希望在页面上呈现某个值,请检查它是否在 data
中。 字节必须与字节进行比较。 如果要比较文本,请改用 get_data(as_text=True)
。
pytest.mark.parametrize
告诉 Pytest 使用不同的参数运行相同的测试函数。 您可以在此处使用它来测试不同的无效输入和错误消息,而无需编写相同的代码 3 次。
login
视图的测试与 register
视图的测试非常相似。 session 登录后应该设置 user_id
,而不是测试数据库中的数据。
tests/test_auth.py
在 with
块中使用 client
允许在返回响应后访问上下文变量,例如 session。 通常,在请求之外访问 session
会引发错误。
测试logout
与login
相反。 注销后,session 不应包含 user_id
。
tests/test_auth.py
博客
所有博客视图都使用您之前编写的 auth
夹具。 调用auth.login()
,来自客户端的后续请求将作为test
用户登录。
index
视图应显示有关与测试数据一起添加的帖子的信息。 当以作者身份登录时,应该有一个编辑帖子的链接。
您还可以在测试 index
视图时测试更多身份验证行为。 未登录时,每个页面都会显示登录或注册链接。 登录后,有一个注销链接。
tests/test_blog.py
用户必须登录才能访问 create
、update
和 delete
视图。 登录用户必须是帖子的作者才能访问update
和delete
,否则返回403 Forbidden
状态。 如果具有给定 id
的 post
不存在,则 update
和 delete
应返回 404 Not Found
。
tests/test_blog.py
create
和 update
视图应该为 GET
请求呈现并返回 200 OK
状态。 当POST
请求中发送有效数据时,create
应将新的post数据插入数据库,update
应修改现有数据。 两个页面都应显示有关无效数据的错误消息。
tests/test_blog.py
delete
视图应重定向到索引 URL,并且该帖子不应再存在于数据库中。
tests/test_blog.py
运行测试
一些额外的配置,这不是必需的,但可以将覆盖率较低的运行测试添加到项目的 setup.cfg
文件中。
setup.cfg
[tool:pytest]
testpaths = tests
[coverage:run]
branch = True
source =
flaskr
要运行测试,请使用 pytest
命令。 它将查找并运行您编写的所有测试函数。
$ pytest
========================= test session starts ==========================
platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /home/user/Projects/flask-tutorial, inifile: setup.cfg
collected 23 items
tests/test_auth.py ........ [ 34%]
tests/test_blog.py ............ [ 86%]
tests/test_db.py .. [ 95%]
tests/test_factory.py .. [100%]
====================== 24 passed in 0.64 seconds =======================
如果任何测试失败,pytest 将显示引发的错误。 您可以运行 pytest -v
来获取每个测试函数的列表,而不是点。
要测量测试的代码覆盖率,请使用 coverage
命令运行 pytest 而不是直接运行它。
$ coverage run -m pytest
您可以在终端中查看简单的覆盖率报告:
$ coverage report
Name Stmts Miss Branch BrPart Cover
------------------------------------------------------
flaskr/__init__.py 21 0 2 0 100%
flaskr/auth.py 54 0 22 0 100%
flaskr/blog.py 54 0 16 0 100%
flaskr/db.py 24 0 4 0 100%
------------------------------------------------------
TOTAL 153 0 44 0 100%
HTML 报告允许您查看每个文件中覆盖了哪些行:
$ coverage html
这会在 htmlcov
目录中生成文件。 在浏览器中打开 htmlcov/index.html
以查看报告。
继续部署到生产。