We're sunsetting PodQuest on 2025-07-28. Thank you for your support!
Export Podcast Subscriptions
cover of episode pytest-cov : The pytest plugin for measuring coverage

pytest-cov : The pytest plugin for measuring coverage

2025/1/23
logo of podcast Test & Code

Test & Code

AI Deep Dive AI Chapters Transcript
People
主讲人
Topics
主讲人: 我主要介绍了 pytest-cov 插件和 Coverage.py 的结合使用,以及如何提高 Python 代码的测试覆盖率。Coverage.py 是一个用于测量代码覆盖率的工具,它可以监控程序执行,识别未执行的代码,并生成多种类型的报告,包括文本报告和 HTML 报告。pytest-cov 插件则简化了 Coverage.py 的使用流程,并提供了许多额外的功能,例如子进程支持、XDist 插件支持以及上下文支持。 在实际使用中,我通常先使用 Coverage.py 生成文本报告用于 CI 和本地快速检查,如果发现未覆盖的行较多,则会使用 HTML 报告进行更详细的分析。对于不需要覆盖的代码,我会在代码中添加 `pragma no cover` 注解或在配置文件中列出这些文件。我追求 100% 的代码覆盖率,但会先进行高层次系统测试,再根据实际情况决定是否需要测试未覆盖的代码,并对不需要测试的代码进行记录或排除。 pytest-cov 插件的优势在于它简化了覆盖率分析流程,避免了手动运行 `coverage run` 和 `coverage report` 命令。它还支持子进程和 XDist 插件,并能自动合并并行测试的报告。此外,它还允许设置 `--cov-fail-under` 参数,指定覆盖率下限,低于该下限则测试套件失败。 Coverage.py 的上下文支持功能可以显示每行代码是由哪些测试覆盖的,pytest-cov 插件简化了该功能的配置,并能在 HTML 报告中显示每行代码对应的测试。这在调试时非常有用。总而言之,pytest-cov 和 Coverage.py 的结合使用可以有效提高 Python 代码的测试覆盖率,并简化测试流程。

Deep Dive

Chapters
This chapter introduces Coverage.py, a tool for measuring code coverage in Python. It explains the importance of measuring coverage in both source and test code, highlighting the benefits of identifying missed lines, branches, and the configuration options available for customizing coverage reports. Different reporting options such as text-based and HTML reports are discussed.
  • Coverage.py measures code coverage of Python programs.
  • It identifies executed and unexecuted code parts.
  • Provides per-file and branch coverage reports.
  • Offers various reporting options (text, HTML) and configuration for exclusions.

Shownotes Transcript

让我们开始使用 pytest-cov,这是下载次数最多的 pytest 插件。欢迎来到 Test & Code。本期节目由 HelloPytest(学习 pytest 的最快方法)和 Python Test 社区赞助。更多信息请访问 courses.pythontest.com。pytest-cov 是一个 pytest 插件,它使用 coverage 或 coverage.py 生成代码覆盖率报告。让我们先暂停一下,快速了解一下 coverage.py 是什么。

coverage.py 是你运行 `pip install coverage` 后获得的工具。它是一个用于测量 Python 程序代码覆盖率的工具。它监控你的程序,记录已执行的代码部分,然后分析源代码以识别本可以执行但未执行的代码。那么,为什么你需要代码覆盖率呢?如果我想确保我的测试代码涵盖了我的所有源代码,我可以运行测试时同时运行 coverage 来查看是否遗漏了什么。

此外,coverage 会报告总的覆盖率百分比。但更重要的是,它会按文件报告。这可以按行号显示哪些代码行被遗漏了。

它还可以进行分支覆盖率分析。因此,你可以确保每个分支决策可能性都被命中。它的配置选项也很棒。因此,即使你使用的是你并不关心其覆盖率的供应商框架或库,你也可以排除这些部分,只测试你的代码。它还有很多报告选项。

对于 CI 和快速的本地检查,我通常使用基于文本的报告运行 coverage。即使是在这种情况下,如果我将它与 talks 配合使用,我也经常只对最新版本运行 coverage,而不是对所有 Python 版本运行。当然,如果我的源代码根据 Python 版本进行了一些分支,那么我必须在多个版本上运行它。如果你在代码中确实有类似 Python 的开关,那么也有一些方法可以组合 coverage 报告。

如果报告显示有多行未覆盖,那么我通常不会尝试从命令行报告或终端报告中找出原因。我会依赖 HTML 报告。你可以通过运行 `coverage html` 来打开 HTML 报告。即使初始报告是使用 pytest-cov 插件生成的,这也适用。HTML 报告更容易查看。

你可以很容易地看到缺少什么以及缺少哪些分支等。你需要 100% 的覆盖率吗?在讨论覆盖率时,这个问题总是会提出来。对我来说,是的,我想要 100% 的覆盖率报告,但带有一个星号。星号是什么意思?这是因为如果可以的话,我会编写高级测试来通过 API 进行系统测试。然后我查看覆盖率报告。

然后我决定,未覆盖的代码是否需要测试?它可以删除吗?我应该在高级别测试新代码,还是应该添加子系统、模块或功能级别测试?关键是我需要考虑一下。

如果代码不需要覆盖,那么如果不是很明显,为什么不呢?然后我会记录下来。我会在内联中添加类似 `pragma no cover` 的内容,或者在配置中列出不需要覆盖的文件。

关键是,一旦我的决定做出,我就将其编码,以便未来的覆盖率报告只显示 100% 或其他值。我可以相信这一点。我不必想,“95% 就足够了,因为我知道有些东西没有测试,但这没关系。”不要那样做。这只会让人难以理解。所以,只需对这些决定进行编码,这样 100% 就意味着一切。

100% 的我想覆盖的代码都被覆盖了。你是否也应该对测试代码(不仅仅是源代码)运行测试覆盖率?是的。实际上,我特意提出这一点,因为 pytest 经常用于测试非 Python 的东西。你可以使用 pytest 来测试任何你可以用 Python 访问的东西,这几乎是任何东西。在我的日常工作中,我用它通过外部 API 测试射频通信测试系统。

很多人使用 pytest 来驱动 Playwright 或 Selenium 等用于 Web 应用和 Web API 测试的框架,即使他们测试的对象不是 Python。即使在这些情况下,对测试代码运行 coverage 也很有用。你为什么只想对测试代码运行它?因为测试代码也是代码,而且它很奇怪。

为什么它很奇怪?因为测试代码由许多测试函数和测试类组成,我们实际上并没有自己调用这些东西,pytest 会调用它们。所以有时我们会复制粘贴修改一个测试函数来创建一个新的测试函数,但我们会忘记更改名称,因此其中一个测试函数将不会运行,它会被忽略。

除非我们运行 coverage,否则我们不会看到它。或者,也许我们在某些测试代码中添加了一些逻辑,并且这些逻辑中的一些路径在某些测试套件或测试套件中被命中,但并非所有开关都被命中。我们不希望这样。我假设你的测试代码中有开关和逻辑,因为你希望所有路径都被覆盖。所以这些事情会发生。由于对测试代码运行 coverage 是一件很容易解决的事情,为什么不这样做呢?

那么如何运行它呢?要在测试代码下直接运行 coverage,你可以使用 `coverage run --source=` 运行它,然后是你的源代码所在的位置,例如 `source/tests` 或 `source,tests`,它将获取你的源代码和测试代码,并在其上运行 coverage。然后是 `-m pytest`。所以总的来说是 `coverage run --source=source,tests -m pytest`,然后是你拥有的任何 pytest 标志。

这与只运行 pytest 的行为略有不同,因为它有点像运行 `python -m pytest`,这会将当前目录添加到搜索路径中。如果将当前目录添加到搜索路径对你来说不是问题,或者你根本不知道我在说什么,那么不用担心。但还没有报告。它只是运行它。

现在你必须运行 `coverage report` 来获取终端报告,或者运行 `coverage html` 来获取 HTML 报告。或者像我一样,先从终端报告开始,如果需要的话再进行 HTML 报告。这并不难,但它分两步。这就是我为什么喜欢 pytest-cov 插件的部分原因。是的,我们回到了我们一开始想谈论的插件。pytest-cov 非常棒。

首先,你可以只运行 `pytest`,然后传递你想要覆盖的内容,例如 `--cov=source` 或 `--cov=tests`,而不是运行 `coverage run -m pytest` 然后 `coverage report`。

这实际上并没有减少多少输入,但我觉得输入少了,至少对我来说是这样。如果我想的话,我可以将这些命令行标志放在配置文件中。实际上,我可能正在使用 talks,所以我可能已经在文件中,talks 文件中有了所有这些内容。

那么,为什么我关心运行插件而不是只运行 coverage 本身呢?我不知道。它看起来更容易。但这不仅仅是为了方便。pytest-cov 插件还带来了许多其他功能。它做的其他事情之一是处理子进程支持。因此,你可以在子进程中分叉内容,而无需你额外的工作,这些内容也会被覆盖。

XDist 也一样。pytest-xdist 是另一个允许你并行运行测试的插件。如果你使用 coverage 来做这件事,你必须组合输出,

然后是 pytest,但 pytest-cov 会自动执行此操作。pytest-cov 会正确地组合报告。因此,如果你使用 XDist,你只需获得最终报告,它就是正确的。人们喜欢这个插件的另一个原因。此外,pytest-cov 不会将当前 DER 添加到搜索路径中。所以如果你关心这一点,请使用这个插件。我还有一些原因喜欢它。你可以设置

一个 `--cov-fail-under` 标志,例如,如果你将其设置为 100 或你想要的任何值,但假设我将其设置为 100。这意味着任何覆盖率低于 100% 的测试套件都会使套件失败。没有测试会失败,但套件会失败,因为它没有达到 100%。这太棒了。你可以将其设置为 95 或你想要的任何值。到目前为止,这些看起来像是微小的改进,但事实并非如此。所有这些额外的东西

累积起来,它们节省了人们的时间和精力,这是一个足够好的理由,解释了为什么 pytest-cov 是下载次数最多的插件,但还有另一件事,上下文

coverage.py 最近(我不知道,在过去一两年内)添加了上下文支持。这是什么意思?这意味着你可以找出每行被覆盖的代码来自哪个测试。所以它有点像,你知道,你可以运行你的,运行 pytest 只是用你的一些测试。

也许像你的一个测试或类,然后查看它命中的所有内容,并确保它命中了你认为它将覆盖的源代码。或者你可以运行你的整个套件,对于每一行代码,你可以看到有多少测试命中了它。这很有趣。但是当你调试出错的原因时,它通常非常有价值。但是要让它工作起来有点麻烦,但 pytest-cov 除外。

使用这个插件,设置起来非常容易。然后,当你完成设置后,你会得到一个 HTML 报告,它在右侧有一列,对于每一行代码,你都可以看到,你移到右边,有一个下拉菜单,你可以看到所有命中该行代码的测试。这太酷了。我不总是使用它,但是当我需要它的时候,我真的很需要它。

pytest-cov 非常棒的另一个原因。所以一定要试试。还有一个关于如何设置上下文内容的教程。它并不难,一个简短的教程,但在 pytest-cov 文档中。所以我会在节目说明中添加一个链接。那么,我们要感谢谁呢?coverage.py 由 Ned Batchelter 维护。谢谢你,Ned。太棒了。他长期以来一直支持它。

pytest-cov 由 Yonel Christian Mariesch 维护。我现在可能把他的姓氏拼错了,但我真的很想感谢 Yonel(I-O-N-E-L),因为他在他的关于页面或博客的某个地方添加了他的发音提示。无论如何,很酷。pytest-cov 和 coverage.py,我都喜欢它们。此外,pytest-cov 也由其他人维护,不仅仅是 Yonel。

多年来,许多人都维护过它并为它添加了功能。它是 pytest 开发组的一部分。无论如何,链接都在节目说明中。看看吧。感谢收听。

感谢收听,并感谢所有通过购买课程支持本节目的人,包括 Hello Pytest(学习 pytest 的最快方法)和完整的 pytest 课程(如果你想真正成为 pytest 专家)。两者都可以在 courses.pythontest.com 获取,你也可以在那里加入 Python 测试社区。现在就到这里。现在去测试一些东西吧。