python - pytest-cov 如何报告由于 pexpect.spawn 而执行的 python 代码的覆盖率?
问题描述
我有一个 Python 项目,它使用 pytest-cov 进行单元测试和代码覆盖率测量。
我的项目的目录结构是:
rift-python
+- rift # The package under test
| +- __init__.py
| +- __main__.py
| +- cli_listen_handler.py
| +- cli_session_handler.py
| +- table.py
| +- ...lots more...
+- tests # The tests
| +- test_table.py
| +- test_sys_2n_l0_l1.py
| +- ...more...
+- README.md
+- .travis.yml
+- ...
我使用 Travis 运行pytest --cov=rift tests
每次签入,并使用 codecov 查看代码覆盖率结果。
被测包提供了一个命令行界面(CLI),它从标准输入读取命令并在标准输出上产生输出。它以python rift
.
测试目录包含两种类型的测试。
第一类测试是测试单个类的传统单元测试。例如,测试 test_table.py 导入 table.py,并执行传统的 pytest 测试(使用 assert 等)。代码覆盖率测量对这些测试按预期工作:codecov 准确报告 rift 包中的哪些行被或未被覆盖测试。
# test_table.py (codecov works)
import table
def test_simple_table():
tab = table.Table()
tab.add_row(['Animal', 'Legs'])
tab.add_rows([['Ant', 6]])
...
tab_str = tab.to_string()
assert (tab_str == "+--------+------+\n"
"| Animal | Legs |\n"
"+--------+------+\n"
"| Ant | 6 |\n"
"+--------+------+\n"
...
"+--------+------+\n")
第二种测试使用 pexpect:它用于pexpect.spawn("python rift")
启动 rift 包。然后它pexpect.sendline
用于将命令注入 CLI (stdin) 并用于pexpect.expect
检查 CLI (stdout) 上命令的输出。测试功能运行良好,但 codecov 没有报告这些测试的代码覆盖率。
# test_sys_2n_l0_l1.py (codecov does not pick up coverage of rift package)
# Greatly simplified example
import pexpect
def test_basic():
rift = pexpect.spawn("python rift")
rift.sendline("cli command")
rift.expect("expected output") # Throws exception if expected output not seen
问题:如何获得代码覆盖率测量结果以报告生成的裂痕包中的覆盖线,以使用 pexpect 进行第二类测试?
注意:我省略了几个我认为不相关的细节,完整的源代码在https://github.com/brunorijsman/rift-python(更新:这个 repo 现在包含答案中建议的工作解决方案)
解决方案
用于coverage run
运行您的 pexpect 程序并收集数据:
如果你通常这样做:
pexpect.spawn("python rift")
然后改为:
pexpect.spawn("coverage run rift.py")
(来源)
测试后,您可能希望将 pexpect 结果与“常规”单元测试结果结合起来。 coverage.py
可以将多个文件合二为一进行报告。
一旦您创建了许多这些文件,您可以将它们全部复制到一个目录中,然后使用combine
命令将它们组合成一个.coverage
数据文件:
$ coverage combine
(来源)
测试中的两个额外细节:
在此示例的测试程序 (test_sys_2n_l0_l1.py) 中,您必须确保在终止 pexpect 生成的时刻和终止测试本身的时刻之间存在延迟。否则,coverage 将没有时间将结果写入 .coverage。我添加了一个睡眠(1.0)。
使用“coverage run --parallel-mode rift”。这需要 (a) 确保 .coverage 不会被以后的运行覆盖,并且 (b) 使“coverage combine”工作(由“pytest --cov”自动运行)
推荐阅读
- rust - 为什么我会收到错误“const fn 中的函数指针不稳定”,但是当包装在新类型中时它会消失?
- r - 使用 R 在 Power BI 中渲染动态 geom_hlines 时遇到问题
- python - 在 python 多处理中共享数据框
- python - 在 post_save 信号后将用户添加到组
- arrays - 这个与对象函数数组相关的Delphi代码可以变得更干净吗?
- flutter - Flutter TabBar 自定义指标
- python - 使用 Python SQLAlchemy 添加、删除和比较值到列
- javascript - 尝试选择 `button` 元素以添加 onClick 事件
- java - 如何将 android 进度条(水平)的颜色更改为不同的部分?
- google-maps - 是否可以从静态地图图片中提取坐标?