【问题标题】:How can pytest-cov report coverage of python code that is executed as a result of pexpect.spawn?pytest-cov 如何报告由于 pexpect.spawn 而执行的 python 代码的覆盖率?
【发布时间】:2018-08-02 22:19:27
【问题描述】:

我有一个 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 开头。

tests 目录包含两种类型的测试。

第一类测试是测试单个类的传统单元测试。例如,测试 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

问题:如何获取代码覆盖率测量结果以报告生成的 rift 包中使用 pexpect 进行第二类测试的覆盖线?

注意:我省略了几个我认为不相关的细节,https://github.com/brunorijsman/rift-python 的完整源代码(更新:这个 repo 现在包含答案中建议的工作解决方案)

【问题讨论】:

  • 查看the answer I gave to a similar question(测量由外部命令生成的服务器的代码覆盖率)。有一个固定装置的工作示例,它更新由pytest-cov 测量的覆盖率,必须与您想要的几乎相同。
  • @hoefling 感谢您的指点。这很有用,如果我的场景变得更复杂,我可能需要它。

标签: python unit-testing pytest pexpect pytest-cov


【解决方案1】:

使用coverage run 运行您的 pexpect 程序并收集数据:

如果你经常这样做:

pexpect.spawn("python rift")

然后改为:

pexpect.spawn("coverage run rift.py")

(Source)

测试后,您可能希望将 pexpect 结果与“常规”单元测试结果结合起来。 coverage.py 可以将多个文件合并为一个进行报告。

一旦您创建了许多此类文件,您可以将它们全部复制到一个目录中,然后使用combine 命令将它们组合成一个.coverage 数据文件:

$ coverage combine

(Source)

另外两个测试细节:

  • 在此示例中的测试程序 (test_sys_2n_l0_l1.py) 中,您必须确保在终止 pexpect 生成的时刻和终止测试本身的时刻之间存在延迟。否则,coverage 将没有时间将结果写入 .coverage。我添加了一个 sleep(1.0)。

  • 使用“覆盖运行 --parallel-mode rift”。这需要 (a) 确保 .coverage 不会被以后的运行覆盖,并且 (b) 使“coverage combine”工作(由“pytest --cov”自动运行)

【讨论】:

    【解决方案2】:

    你基本上必须启用subprocess coverage tracking

    我建议使用https://pypi.org/project/coverage_enable_subprocess/ 轻松启用此功能。

    建议/需要使用parallel = 1,并且您必须导出COVERAGE_PROCESS_START,例如export COVERAGE_PROCESS_START="$PWD/.coveragerc".

    【讨论】:

      猜你喜欢
      • 2019-08-12
      • 2018-07-17
      • 1970-01-01
      • 2019-09-19
      • 2017-03-23
      • 2017-04-01
      • 2012-10-05
      • 2013-07-26
      • 2014-08-21
      相关资源
      最近更新 更多