控制 PHPUnit 测试执行:仅运行特定命名模式的测试类

控制 PHPUnit 测试执行:仅运行特定命名模式的测试类

本文旨在解决 PHPUnit 在复杂项目或非标准代码结构中可能遇到的测试执行范围问题,特别是当您希望仅运行类名以 “Test” 结尾的测试时。文章将详细介绍两种主要解决方案:通过重命名非测试方法或修改其可见性来避免其被执行,以及如何实现自定义 TestSuiteLoader 以实现更精细的测试类加载控制,确保 PHPUnit 仅执行符合特定命名约定的测试。

phpunit 的测试实践中,我们通常期望只有明确标记为测试的类和方法才会被执行。然而,在某些情况下,例如在一个文件中定义了多个类(不遵循 psr 标准),或者辅助类意外地继承了 testcase 并包含以 test 开头的方法时,phpunit 可能会执行不应该运行的测试,导致测试结果不准确。例如,一个名为 examplehelper 的类,即使它是一个辅助类,如果它继承自 exampletest 并且包含 testshouldnotbeexecuted 方法,phpunit 默认情况下会将其识别为测试并执行。本文将深入探讨如何解决这一问题,确保 phpunit 按照您的预期精确地执行测试。

1. PHPUnit 测试发现机制概述

PHPUnit 默认通过以下规则发现并执行测试:

测试类: 通常是继承自 PHPUnitFrameworkTestCase 的类。测试方法: 类中所有公共的、以 test 开头的方法。

这种机制在大多数情况下工作良好,但当一个文件中存在多个类,且非测试类也意外地满足上述条件时,就会出现问题。

2. 解决方案一:重命名非测试方法或修改其可见性

这是最直接且通常最简单的解决方案,适用于您可以修改相关代码文件的情况。PHPUnit 官方文档明确指出,测试方法是公共的且以 test 开头。因此,只要不满足这些条件,方法就不会被自动识别为测试。

问题示例:

立即学习“PHP免费学习笔记(深入)”;

assertSame(0, 0);  }}final class ExampleHelper extends ExampleTest // 继承了测试类{  public function testShouldNotBeExecuted(): void // 方法名以 'test' 开头  {    $this->assertSame(0, 1); // 这个断言会失败  }}

在这个例子中,ExampleHelper 类虽然是一个辅助类,但由于它继承了 ExampleTest 并且包含一个公共的 testShouldNotBeExecuted 方法,PHPUnit 会将其识别为一个测试并执行,导致不期望的失败。

解决方案:

重命名方法: 将非测试方法的名字更改为不以 test 开头。

final class ExampleHelper extends ExampleTest{  public function doSomethingElse(): void // 方法名不再以 'test' 开头  {    $this->assertSame(0, 1); // 不会被执行  }}

修改方法可见性: 将非测试方法的可见性从 public 改为 protected 或 private。

final class ExampleHelper extends ExampleTest{  protected function testShouldNotBeExecuted(): void // 方法不再是公共的  {    $this->assertSame(0, 1); // 不会被执行  }}

优点:

简单易行,理解成本低。直接遵循 PHPUnit 的测试发现规则。

缺点:

需要修改现有代码,这在大型遗留项目中可能不切实际或成本高昂。如果辅助类确实需要继承测试类,且其方法名无法更改或可见性不能降低,则此方法不适用。

3. 解决方案二:实现自定义 TestSuiteLoader

当修改现有代码不可行,或者您需要更精细地控制哪些类被加载为测试套件时,实现一个自定义的 TestSuiteLoader 是一个强大的选择。TestSuiteLoader 负责根据文件路径加载类并确定它们是否为测试套件的一部分。

核心思想:通过自定义 TestSuiteLoader,我们可以拦截 PHPUnit 加载测试类的过程,并在加载前根据我们定义的规则(例如,类名必须以 “Test” 结尾)进行过滤。

实现步骤:

创建自定义 TestSuiteLoader 类:创建一个新的 PHP 类,例如 CustomTestSuiteLoader.php,并实现 PHPUnitRunnerTestSuiteLoader 接口或继承其默认实现。

// tests/CustomTestSuiteLoader.phpgetName(), 'Test')) {                // 如果类名不以 'Test' 结尾,且它是一个 TestCase,                // 我们可以尝试返回一个不包含任何测试方法的 ReflectionClass,                // 或者抛出一个特殊的异常让 PHPUnit 忽略。                // 简单地让它不被识别为测试类是最好的。                // PHPUnit 内部的 TestSuiteBuilder 会检查类是否是 TestCase 的子类。                // 如果我们在这里返回一个非 TestCase 的 ReflectionClass,它就不会被添加。                // 但由于 ExampleHelper 继承了 ExampleTest (TestCase),这行不通。                // 最直接且符合 PHPUnit 9.x 语义的方法是:                // 如果类名不以 'Test' 结尾,我们让它被加载,                // 但 PHPUnit 的 TestSuiteBuilder 在构建 TestSuite 时,                // 会检查类是否是 TestCase 的子类。                // 问题在于 ExampleHelper *是* TestCase 的子类。                // 所以我们需要在 TestSuiteBuilder 层面进行过滤,而不是在 TestSuiteLoader 层面。                // 重新思考:TestSuiteLoader 负责“加载”类,而不是“过滤”类。                // 过滤应该发生在 TestSuiteBuilder 构建测试套件时。                // 原始答案的意图是使用 TestSuiteLoader 来控制哪些类被“加载”为测试。                // 这意味着我们可以让它只加载类名以 Test 结尾的类。                // 如果一个文件包含多个类,且其中一个不以 Test 结尾,                // 并且它不应该被视为测试,那么 DefaultTestSuiteLoader 会加载它。                // 我们的目标是,即使它被加载了,也不被当作测试运行。                // 鉴于此,CustomTestSuiteLoader 的作用是限制哪些类被 *认为* 是测试类。                // DefaultTestSuiteLoader 的 load 方法返回一个 ReflectionClass。                // 如果我们在这里判断,并且不返回符合条件的 ReflectionClass,                // 那么 PHPUnit 的 TestSuiteBuilder 就不会将其加入测试套件。                // 但是,load 方法的签名要求我们返回 ReflectionClass。                // 这意味着我们不能简单地“不加载”它。                // 解决方案应该是在 TestSuiteBuilder 中进行过滤,或者让 TestSuiteLoader 返回一个“空”的测试类。                // 考虑到 PHPUnit 的内部机制,直接修改 TestSuiteLoader 来过滤类名是比较困难的。                // 除非我们能修改 TestSuiteBuilder,或者在 TestSuiteLoader 中做一些“欺骗”行为。                // 实际可行的办法是:让 CustomTestSuiteLoader 继承 DefaultTestSuiteLoader,                // 并重写其 load 方法,在内部进行判断。                // 如果类名不符合,我们仍然返回 ReflectionClass,                // 但希望 PHPUnit 的 TestSuiteBuilder 在构建 TestSuite 时能忽略它。                // 遗憾的是,TestSuiteLoader 接口的主要目的是加载类,而不是过滤。                // 重新审视原始答案:                // "Write a custom TestSuiteLoader and declare it in the configuration"                // 这暗示 TestSuiteLoader 能够影响哪些类被识别为测试。                // 查阅 PHPUnit 9.5 的 TestSuiteLoader 接口和 DefaultTestSuiteLoader 实现。                // DefaultTestSuiteLoader 的 load 方法确实只负责加载文件并返回 ReflectionClass。                // 过滤逻辑通常在 TestSuiteBuilder 的 addTestSuite 方法中。                // 既然如此,我们不能直接在 load 方法中阻止加载,因为签名要求返回 ReflectionClass。                // 那么,CustomTestSuiteLoader 应该如何实现过滤呢?                // 也许它的作用是影响哪些文件被视为包含测试。                // 或者,它可以在加载后,返回一个“假的”ReflectionClass,                // 或者一个不包含任何测试方法的 ReflectionClass。                // 这会比较 hacky。                // 另一种思路是,CustomTestSuiteLoader 可以影响 PHPUnit 如何查找测试类。                // 如果我们有一个 TestSuiteLoader,它只知道如何加载以 'Test' 结尾的类,                // 那么即使 ExampleHelper 存在,它也不会被加载。                // 让我们假设 CustomTestSuiteLoader 的目标是:                // 1. 加载文件。                // 2. 找到文件中所有的类。                // 3. 仅返回那些类名以 'Test' 结尾的 ReflectionClass。                // 但 TestSuiteLoader::load 接收一个 $className 参数,这表示它已经被识别为特定的类。                // 考虑到问题的核心是“在类名不以 Test 结尾的类中不运行测试”,                // 即使这些类继承了 TestCase,那么 TestSuiteLoader 可能不是最直接的过滤点。                // PHPUnit 的 TestSuiteBuilder 负责构建 TestSuite。                // 我们可以考虑自定义 TestSuiteBuilder,但这超出了 TestSuiteLoader 的范畴。                // 原始答案的意图可能是:                // 如果 CustomTestSuiteLoader 仅加载那些符合命名约定的类,                // 那么其他类就不会被传递给 TestSuiteBuilder。                // 但 DefaultTestSuiteLoader 的 load 方法是针对一个特定的 $className。                // 让我尝试一种更符合 PHPUnit 内部逻辑的 CustomTestSuiteLoader 实现,                // 它会尝试加载文件,但如果类名不符合,则会抛出异常,                // 从而阻止 PHPUnit 将其视为有效的测试类。                // 这确实是 `PHPUnitRunnerTestSuiteLoader` 接口的预期行为之一:                // 如果加载失败(例如,类不存在或不符合预期),可以抛出异常。                throw new PHPUnitRunnerException(                    sprintf(                        'Class "%s" does not end with "Test" and will not be loaded as a test.',                        $className                    )                );            }        }        return parent::load($filename, $className);    }}

重要提示: 上述 CustomTestSuiteLoader 的实现方式可能需要根据 PHPUnit 的具体版本和内部实现进行微调。在某些 PHPUnit 版本中,TestSuiteLoader::load 期望始终返回一个 ReflectionClass。如果直接抛出异常,PHPUnit 可能会将文件标记为加载失败,而不是简单地忽略该类。更稳健的做法是,如果类不符合条件,但文件中有其他符合条件的类,我们仍然希望加载文件。因此,更精确的过滤可能需要在 TestSuiteBuilder 层面,或者通过 PHPUnit 的 TestRunner 钩子。

然而,为了遵循原始答案的建议,并假设 PHPUnit 的 TestSuiteLoader 可以用于此目的,我们可以尝试让它在不符合条件时抛出异常。如果一个文件包含多个类,且其中一个类名不以 Test 结尾,但文件中有其他类名以 Test 结尾的类,那么这种方法可能会阻止整个文件被加载。

更实际的 CustomTestSuiteLoader 方案(针对 PHPUnit 9.5+):TestSuiteLoader 的核心职责是加载类。它不直接负责过滤哪些类是“测试”。过滤逻辑通常在 TestSuiteBuilder 中,它会检查一个类是否是 TestCase 的子类。如果我们的目标是“仅运行类名以 Test 结尾的类中的测试”,并且 ExampleHelper 继承了 ExampleTest (一个 TestCase),那么 TestSuiteBuilder 会认为 ExampleHelper 是一个测试类。

因此,仅仅在 TestSuiteLoader 中检查类名可能不足以解决问题。一个更有效的策略是:

确保非测试类不继承 TestCase。 如果 ExampleHelper 不继承 ExampleTest,那么它就不会被视为测试类。结合解决方案一: 即使 ExampleHelper 继承了 TestCase,只要它的方法不以 test 开头,PHPUnit 也不会执行它们。

如果必须使用 TestSuiteLoader 来解决此问题,这意味着我们可能需要一个能够影响 TestSuiteBuilder 行为的 TestSuiteLoader。然而,TestSuiteLoader 接口本身并没有提供这样的机制。

重新评估: 鉴于 PHPUnit 9.x 的设计,TestSuiteLoader 主要负责将类文件加载到内存中并返回其 ReflectionClass。它本身并不直接负责“过滤”哪些类是测试类。过滤逻辑是在 TestSuiteBuilder 中完成的,它会检查 ReflectionClass 是否是 TestCase 的子类,以及是否包含 test 开头的方法。

因此,如果一个类(如 ExampleHelper)继承了 TestCase 并且包含 test 开头的方法,那么它仍然会被 TestSuiteBuilder 识别为测试类,无论 TestSuiteLoader 如何实现。

结论: 原始答案中关于“自定义 TestSuiteLoader”的建议,在 PHPUnit 9.x+ 版本中,对于“过滤掉继承了 TestCase 但类名不以 Test 结尾的类中的测试方法”这一具体问题,可能不是最直接或最有效的解决方案。它更适用于控制哪些文件被加载,而不是控制文件内哪些类被视为测试。

但是,为了完整性,如果一个自定义 TestSuiteLoader 能够影响 TestSuiteBuilder 的行为,理论上它可能通过返回一个特殊的 ReflectionClass 或者在加载时进行更深层次的检查。然而,这通常需要深入到 PHPUnit 的私有 API,这不推荐。

更符合 PHPUnit 哲学且能够实现类名过滤的方案,通常是在 phpunit.xml 配置中使用 的 suffix 或 prefix 属性,但这仅适用于文件名,而不是类名。

如果必须仅根据类名来过滤,并且不能修改代码,那么最接近的“配置”解决方案可能是:

使用 PHPUnit 的 exclude 选项: 在 phpunit.xml 中排除特定的文件或目录。但这不够灵活,无法根据类名过滤。利用 PHPUnit 的 bootstrap 文件进行运行时过滤: 在 bootstrap.php 中,我们可以尝试在 PHPUnit 开始测试之前,动态地移除或修改不符合条件的测试类或方法。但这会非常复杂且侵入性强。

鉴于原始答案明确提到了 TestSuiteLoader,我们假设存在一种方式可以利用它。最接近的解释是,TestSuiteLoader 可能会在加载过程中抛出异常,从而阻止不符合条件的类被识别为测试。

// tests/CustomTestSuiteLoader.phpgetShortName(), 'Test')) {            // 如果类名不符合约定,并且它是一个 PHPUnit 的测试类 (继承了 TestCase)            // 那么我们抛出异常,阻止 PHPUnit 将其识别为有效的测试类。            // 这将导致 PHPUnit 跳过此类的测试。            // 注意:这会阻止该类中的所有测试,即使有符合命名约定的方法。            // 并且如果同一个文件中有其他符合条件的测试类,也可能受到影响。            // 这是对 TestSuiteLoader 行为的一种强行解释,其效果可能因 PHPUnit 版本而异。            if ($reflection->isSubclassOf(TestCase::class)) {                throw new PHPUnitRunnerException(                    sprintf(                        'Class "%s" does not end with "Test" and will not be loaded as a test suite.',                        $className                    )                );            }        }        return $reflection;    }}

配置 phpunit.xml 使用自定义 TestSuiteLoader:在您的 phpunit-config.xml 文件中,添加 testSuiteLoaderClass 属性,指向您的自定义加载器。

            ./tests      </

以上就是控制 PHPUnit 测试执行:仅运行特定命名模式的测试类的详细内容,更多请关注php中文网其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1322823.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
float浮动造成父级元素塌陷的解决方法
上一篇 2026年5月10日 11:06:34
如何安装php性能分析工具_blackfire等性能分析工具配置方法教程
下一篇 2026年5月10日 11:06:37

相关推荐

  • Go语言全局日志器Lumber的配置与使用

    本文将详细介绍在go语言中,如何通过声明包级别变量的方式,实现`github.com/jcelliott/lumber`等日志库的全局访问。这种方法允许在`main`函数外部的任何函数中方便地使用日志器,避免了重复声明,并确保日志器在程序启动时正确初始化,从而提升代码的可维护性和日志管理的便捷性。 …

    2026年5月10日
    000
  • 基于用户语言环境定制 Laravel 通知

    本文介绍了如何在 Laravel 框架中,根据用户的语言环境(locale)发送定制化的通知。通过将用户语言环境信息传递给通知类,并在通知构建过程中动态设置应用语言环境,确保通知内容以用户偏好的语言呈现。同时,也介绍了使用 Laravel 内置的通知本地化功能来实现相同目标的方法。 在 Larave…

    2026年5月10日
    000
  • 如何在点击的Div中获取正确的ID

    本文旨在解决动态生成的HTML元素中,点击事件发生时,如何准确获取与该元素关联的ID值的问题。通过事件委托和DOM遍历,我们将提供一种可靠的方法,确保在复杂的动态环境中,始终能获取到正确的ID,避免因选择器错误而导致的数据获取错误。 在动态生成的HTML结构中,经常会遇到点击事件需要获取特定ID的情…

    2026年5月10日
    000
  • React组件跨域导出与样式封装指南

    本文详细阐述了如何将React组件及其样式安全地导出并嵌入到外部Web页面中,解决了传统方法中样式丢失和命名冲突的问题。通过利用Webpack进行样式内联打包以及CSS Modules实现样式隔离,确保组件在外部环境中保持其预期的视觉效果,同时避免对宿主页面的影响,提供了一套专业且高效的解决方案。 …

    2026年5月10日
    100
  • React组件间事件处理器与状态传递:从父组件到多级子组件的实践指南

    本文探讨在React中如何高效地将事件处理器或其产生的状态从父组件传递给子组件,特别是涉及多级嵌套的情况。文章将详细阐述直接传递事件处理函数和通过状态管理传递事件结果的两种核心模式,并提供清晰的代码示例与注意事项,帮助开发者构建响应式用户界面。 理解React组件通信基础:Props 在React中…

    2026年5月10日
    000
  • Go语言:高效读取文本文件并按行处理的全面指南

    本教程详细介绍了在go语言中读取文本文件并将其内容按行存储到字符串切片中的两种主要方法。我们将探讨使用`ioutil.readfile`结合`strings.split`的简洁方式,以及利用`bufio.scanner`进行高效逐行处理的策略,并提供相应的代码示例和最佳实践,帮助开发者根据文件大小和…

    2026年5月10日
    000
  • 实现水平滚动文本的淡出效果

    实现水平滚动文本的淡出效果实现水平滚动文本的淡出效果实现水平滚动文本的淡出效果实现水平滚动文本的淡出效果

    本文将介绍如何使用 CSS 实现水平滚动文本的淡出效果,尤其是在非均匀背景下,传统线性渐变方案不适用的情况下。我们将通过结合 linear-gradient 和 background-clip 属性,创建一个在水平滚动时两侧逐渐淡出的文本效果。 实现原理 核心思路是利用 CSS 的 linear-g…

    2026年5月10日 用户投稿
    200
  • FastAPI 中如何解析用逗号分隔的多个 Query 参数?

    在fastapi中,默认情况下,同名query参数会被合并成一个列表。例如,请求?source=manual&source=vdna将得到source=[‘manual’, ‘vdna’]。 如果你希望使用逗号分隔多个query参数的值,有两种…

    2026年5月10日
    000
  • 怎么用php登录_PHP用户登录验证与身份认证方法

    答案:常见PHP登录验证方法包括基于Session的用户状态跟踪、Token认证、密码哈希存储、验证码防破解及HTTPS安全设置。首先启动session并验证用户凭证,匹配后设置$_SESSION[‘user_id’]标识登录;后续请求通过检查会话变量判断登录状态。对于API…

    2026年5月10日
    000
  • 高效计算区间内可整除数值的数量

    本文探讨了如何在指定范围 `[0, max)` 内高效地计算能被给定 `divisor` 整除的数值数量。我们将对比迭代循环和数学公式两种方法,并详细解释数学公式的推导过程,展示其在性能上的显著优势,尤其适用于处理大规模数据,从而提供一个更优的解决方案。 在编程实践中,我们经常需要解决一类问题:统计…

    2026年5月10日
    300
  • 探索教师库:结构化非结构化数据(以及沿途的一些乐趣)

    我最近访问了讲师库,不得不说,给我留下了深刻的印象。结构化非结构化数据的概念非常强大,而且我敢说,有点神奇。你可以获取无处不在的数据并以某种方式对其施加秩序——嗯,这就是我的魔法。 但是……它到底是如何工作的? 为了找到答案,我花了一些时间深入研究这个库的内部结构,我发现幕后有两个关键人物对它的大部…

    2026年5月10日
    000
  • 正则表达式:精确匹配所需字符串,排除其他干扰

    本文旨在帮助读者理解如何编写更精确的正则表达式,以从一组字符串中提取特定模式,同时避免不必要的匹配。通过分析一个实际案例,我们将学习如何使用否定预查、非捕获组和字符类等技巧,来优化正则表达式,使其更符合需求。 正则表达式是一种强大的文本处理工具,但编写一个既能匹配目标字符串,又能排除其他类似字符串的…

    用户投稿 2026年5月10日
    000
  • JavaScript定时器实现多图片同步切换教程

    本教程详细讲解如何利用JavaScript的setInterval函数,实现网页中多张图片(如背景图、号召性用语图和顶部图)的同步循环切换。通过维护一个共享的索引,确保所有图片在预设的时间间隔内,按照各自的图片序列同时更新,从而创建流畅且一致的视觉动态效果。 引言 在网页设计中,动态视觉效果能够极大…

    2026年5月10日
    000
  • 掌握CSS层叠上下文:将下拉菜单叠加在地图之上

    本文将深入探讨如何利用css的position和z-index属性,解决将下拉菜单等交互元素精确叠加在全屏背景元素(如地图)上方的问题。通过调整元素的定位方式和层叠顺序,确保下拉菜单在视觉上处于地图之上,实现更灵活和用户友好的界面布局。 在现代网页设计中,将交互式UI元素(如下拉菜单、模态框)叠加在…

    2026年5月10日
    200
  • 使用 Pandas 的 isin 方法进行日期匹配时出现问题的解决方案

    本文旨在解决在使用 Pandas 的 `isin` 方法,结合 `datetime` 对象进行数据筛选时,可能遇到的 `TypeError` 和 `AttributeError` 问题。通过分析问题代码,我们将提供清晰的解决方案,并解释其背后的原因,帮助读者避免类似错误,高效地进行数据处理。 在使用…

    2026年5月10日
    000
  • CSS id 选择器

    [导读] id 选择器id 选择器可以为标有特定 id 的 html 元素指定特定的样式。id 选择器以 ” ” 来定义。下面的两个 id 选择器,第一个可以定义元素的颜色为红色,第二个定义元素的颜色为绿色: red {color:re id 选择器 id 选择器可以为标有特…

    用户投稿 2026年5月10日
    000
  • PHP对象数组遍历与动态内容生成教程

    本教程旨在解决php中遍历对象数组时,如何正确访问每个对象的动态属性,并利用循环索引生成html元素(如轮播图指示器)的问题。我们将探讨使用`foreach`循环直接访问对象属性和索引,以及创建新数组并为每个对象注入索引值两种方法,并提供最佳实践建议,以确保代码的健壮性和安全性。 动态内容生成与PH…

    2026年5月10日
    000
  • 如何安装php性能分析工具_blackfire等性能分析工具配置方法教程

    首先安装Blackfire PHP扩展并配置客户端凭证,再通过CLI或Web请求启用性能检测;随后安装XHProf进行轻量级追踪,并集成Tideways实现可视化分析。 如果您在优化PHP应用性能时需要深入分析代码执行过程,定位耗时操作,则可以借助专业的性能分析工具如Blackfire进行精细化监控…

    2026年5月10日
    000
  • React应用登录后重定向失败的常见原因与解决方案

    本文旨在探讨React应用中用户登录后无法正确重定向至主页的常见问题。核心原因在于状态管理与组件生命周期中的时序问题,即loggedIn状态未在导航前及时更新。通过在成功登录后立即更新loggedIn状态,并结合useEffect的正确使用,可以有效解决此问题,确保用户体验的流畅性。 在构建现代We…

    2026年5月10日
    000
  • 如何用Python进行机器学习?

    在python中进行机器学习可以分为以下几个步骤:1. 数据处理和分析,使用numpy和pandas处理数据集。2. 选择机器学习模型,使用scikit-learn进行模型训练和评估。3. 深度学习,使用tensorflow或pytorch构建和训练神经网络。4. 模型调参,使用交叉验证和网格搜索优…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信