pytest-cov如何报告由pexpect.spawn执行的Python代码的覆盖率?

4

我有一个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(标准输入),并使用 pexpect.expect 检查CLI(标准输出)中的命令输出。测试功能正常,但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获取代码覆盖率测量结果,以报告第二类测试中生成的rift软件包中的已覆盖行?

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


1
请查看我对类似问题的回答(测量由外部命令生成的服务器的代码覆盖率)。有一个更新pytest-cov测量到的覆盖率的有效夹具示例,应该与您想要的差不多。 - hoefling
@hoefling 感谢您的指引。这很有用,如果我的情况变得更加复杂,我可能会需要它。 - Bruno Rijsman
2个回答

2

使用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 spawn之后和终止测试本身之前有一个延迟。否则,coverage将没有时间将结果写入.coverage文件。我添加了一个sleep(1.0)。

  • 使用"coverage run --parallel-mode rift"。这是必要的,以确保 .coverage 文件不会被后来的运行覆盖,并使 "coverage combine" 生效(这是由 "pytest --cov" 自动运行的)。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接