9. 代码覆盖率分析

    在本章中,你将学到 PHPUnit 中与代码覆盖率相关的一切功能。通过这部分功能,能够了解在测试运行过程中执行了生产代码的哪些部分。它使用了 组件,而这个组件又使用了 PHP 的 Xdebug 或 扩展或 PHPDBG 所提供的代码覆盖率功能。

    如果在运行测试时看到警告信息说没有可用的代码覆盖率驱动,这意味着你正在使用 PHP 命令行二进制文件()且未加载 Xdebug 或 PCOV。

    PHPUnit 可以生成基于 HTML 的代码覆盖率报告,同时也能生成好几种(Clover、Crap4J、PHPUnit)基于 XML 的代码覆盖率信息记录文件。代码覆盖率信息也能以文本格式提供(同时可以输出到 STDOUT)或以 PHP 代码格式输出以供进一步处理。

    中列出了各种控制代码覆盖率功能的命令行参数供参考,同时<logging> 元素中可以找到其他相关的配置信息。

    目前存在多种软件衡量标准用于衡量代码覆盖率:

    行覆盖率(Line Coverage)

    行覆盖率(Line Coverage)按单个可执行行是否已执行进行计量。

    分支覆盖率(Branch Coverage)

    路径覆盖率(Path Coverage)

    路径覆盖率(Path Coverage)按测试套件运行时函数或者方法内部所经历的执行路径进行计量。一个执行路径指的是从进入函数或方法一直到离开的过程中经过各个分支的特定序列。

    函数与方法覆盖率(Function and Method Coverage)

    类与特质覆盖率(Class and Trait Coverage)

    类与特质覆盖率(Class and Trait Coverage)按单个类或特质的所有方法是否全部已覆盖进行计量。仅当一个类或特质的所有方法全部已覆盖时 php-code-coverage 才将其视为已覆盖。

    变更风险反模式(CRAP)指数(Change Risk Anti-Patterns (CRAP) Index)

    为了告诉 PHPUnit 哪些源代码文件要包含在代码覆盖率报告中,必须配置过滤器。可以用选项 --coverage-filter 或通过配置文件(参见The <include> Element)来完成。

    includeUncoveredFilesInCodeCoverageReportprocessUncoveredFilesForCodeCoverageReport 配置设置可用于配置过滤器的使用方式:

    • (默认值)意味着所有文件都会包括在代码覆盖率报告中,即使文件中没有任何一行代码被执行过也一样
    • processUncoveredFilesForCodeCoverageReport="false"(默认值)意味着没有已执行代码行的文件会被加入到代码覆盖率报告中(如果设置了 includeUncoveredFilesInCodeCoverageReport="true"),但它并不会被 PHPUnit 加载,因此也不会对其进行分析来获取正确的可执行代码行信息

    请注意,当设置了 processUncoveredFilesForCodeCoverageReport="true" 时将对源代码文件进行载入,这在某些情况下可能导致问题,比如,源代码文件包含有处于类或者函数作用域之外的代码。

    有时,一些代码块是无法对其进行测试的,因此希望在代码覆盖率分析中忽略它们。在 PHPUnit 中可以用 @codeCoverageIgnore@codeCoverageIgnoreStart 与 标注来做到这点,如 中所示。

    示例 9.1 使用 @codeCoverageIgnore@codeCoverageIgnoreStart@codeCoverageIgnoreEnd 标注

    代码中被忽略掉的行(用标注标记为忽略)将会计为已执行(如果它们是可执行的),并且不会在代码覆盖情况中被高亮标记。

    @covers 标注(参见annotation documentation)可以用在测试代码中来指明测试类(或测试方法)想要对哪些代码部分进行测试。如果提供了这个信息,则可以有效过滤代码覆盖率报告,仅包含所指定的代码部分中的已执行代码。 展示了一个例子。

    如果用 @covers 标注指定了一个方法吗,那么只有所指方法会被视为已覆盖,这个方法所调用的方法不会视为已覆盖。因此,如果用提取方法重构了已覆盖的方法,则需要添加相应的 @covers 标注。这就是推荐将此标注用在类作用域而非方法作用域的原因。

    示例 9.2 指明了要覆盖的类的测试类

    示例 9.3 指明了要覆盖哪个方法的测试

    同时,可以用 标注来指明一个测试不覆盖任何方法(参见@coversNothing)。这可以在编写集成测试时用于确保代码覆盖全部来自单元测试。

    本节中展示了一些值得注意的边缘情况,在这些边缘情况中可能出现令人迷惑的代码覆盖率信息。