Gutenberg 仓库 PHP 测试(PHPUnit)实战指南:Agent 规则、环境路由与函数前缀测试
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
本篇技术指南聚焦 WordPress 块编辑器项目 Gutenberg 仓库中的PHP 测试(PHPUnit)体系:从 AI Agent 运行 PHP 测试时的规则约定、wp-env 测试环境的启动与状态检查,到composer test/vendor/bin/phpunit的具体执行方式,再到测试内置(带前缀)PHP 函数这一仓库特有的关键约束。读完本文,你将掌握在 Gutenberg 仓库中正确启动、路由、编写与运行 PHPUnit 测试的完整流程,并能依据源码与配置逐层理解其底层原理。
关联文档与适用范围
本指南的主体依据是仓库中的 .agents/skills/testing/references/php.md,它是.agents/skills/testing技能(SKILL.md)针对PHP 测试(PHPUnit)的场景化路由文件。该技能整体覆盖三类测试:JavaScript 单元/集成测试(Jest,见 references/jest.md)、PHP 测试(PHPUnit,即本文主题)、端到端测试(Playwright,见 references/e2e.md)。
php.md 明确声明其适用对象是Agent(AI 编码代理),因此内容强调"规则(rules)与路由(routing)":什么时候能跑、跑什么命令、测试哪些符号、遵守哪些红线。这些约定同样适用于任何在本地复现 PHP 测试流程的开发者。
运行 PHP 测试的前提:wp-env 测试环境
php.md 开门见山给出最重要的一条 Agent 规则:PHPUnit 测试依赖 wp-env 测试环境。流程必须是:
# 1. 先检查环境是否已在运行 npm run wp-env-test status # 2. 仅当环境未运行时才启动 npm run wp-env-test start这条规则在仓库根目录的 AGENTS.md 中被完整复述:"Always check status first. Only start if not already running."(先检查状态,仅在未运行时启动)。对于 Agent 会话而言,反复start会浪费大量时间并可能触发端口冲突,因此"先 status 再 start"是硬性约定。
从 package.json 可以看到wp-env-test脚本的实际定义:
"wp-env-test": "wp-env --config .wp-env.test.json"即所有 wp-env 相关操作都使用独立的测试环境配置 .wp-env.test.json,其关键配置包括:
port: 8889:测试环境专用端口,与开发环境(默认 8888)隔离;core: "WordPress/WordPress":拉取 WordPress 核心用于测试;plugins: ["."]:将当前 Gutenberg 仓库作为插件挂载;themes与mappings:挂载测试主题(./test/emptytheme、gutenberg-test-themes等)和测试用 mu-plugins、测试插件,确保测试运行在接近真实站点的环境中。
为什么 PHP 测试必须依赖 wp-env
Gutenberg 的 PHP 测试继承自 WordPress Core 的 PHPUnit 测试体系,需要一个完整的 WordPress 环境(数据库、核心函数、主题/插件加载机制)才能真正执行。这一点可以从测试引导文件 phpunit/bootstrap.php 得到印证:
- 定义
WP_DEBUG、LOCAL_WP_DEBUG_LOG等调试常量(第 9-23 行),与 WordPress Core 的 PHPUnit 测试保持一致性; - 通过
WP_TESTS_DIR环境变量或回退路径定位 WP 测试库(第 36-49 行); - 通过
_manually_load_plugin()手动加载被测插件lib/load.php(第 61-64 行); - 注册
fail_if_died处理wp_die,避免 WordPress 在测试启动阶段静默死亡导致"假通过"(第 78-85 行); - 通过
$GLOBALS['wp_tests_options']预启用一批实验特性(gutenberg-full-site-editing、gutenberg-dashboard-widgets、gutenberg-real-time-collaboration等,第 87-97 行),使测试能覆盖实验性代码路径。
执行 PHP 测试的两条命令
php.md 给出的运行方式有两条:
# 方式一:运行全部 PHP 测试(含 lint) composer test # 方式二:运行指定测试文件或目录 vendor/bin/phpunit <path_to_test_file.php>composer test 到底做了什么
composer.json 的 scripts 定义如下:
"scripts": { "format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source", "lint": "phpcs --standard=phpcs.xml.dist", "test": "phpunit", "test:watch": "phpunit-watcher watch < /dev/tty" }也就是说composer test直接调用phpunit(使用根目录 phpunit.xml.dist 配置),而composer run test:watch则使用sirbrillig/phpunit-watcher(composer.json 的 require-dev 中包含spatie/phpunit-watcher)实现文件变更自动重跑。
值得注意的是:composer test只跑 PHPUnit,不包含代码风格检查。composer.json 中的lint脚本(phpcs --standard=phpcs.xml.dist)才是 PHP_CodeSniffer 的 PHP 代码规范检查,format脚本(phpcbf)用于自动修复。这与"PHP 测试 + lint"一体化的 npm 命令(见下文)有区别。
vendor/bin/phpunit 直接调用
第二种方式是直接调用 Composer 安装的 PHPUnit 二进制,<path_to_test_file.php>可以是单个测试文件,也可以是目录。例如:
vendor/bin/phpunit phpunit/block-supports/anchor-test.php vendor/bin/phpunit phpunit/block-supports/通过 npm scripts 运行(仓库的完整链路)
虽然 php.md 只给了 composer 两条命令,但仓库在 package.json 中封装了更完整的 npm 链路,理解它有助于把握"composer test"之外的全貌:
"test:php": "npm run lint:php && npm run test:unit:php", "test:unit:php:setup": "wp-env --config .wp-env.test.json start", "test:unit:php:base": "wp-env --config .wp-env.test.json run --env-cwd='wp-content/plugins/gutenberg' wordpress vendor/bin/phpunit -c phpunit.xml.dist --verbose", "test:unit:php": "npm run test:unit:php:setup && npm run test:unit:php:base", "test:php:watch": "wp-env --config .wp-env.test.json run --env-cwd='wp-content/plugins/gutenberg' cli composer run-script test:watch"对照 docs/contributors/code/testing-overview.md 的 PHP testing 一节可以看到官方文档的表述:
npm run test:php:同时执行 PHP lint(PHP_CodeSniffer)与 PHPUnit 单测;npm run test:unit:php:只跑单测、跳过 linter;npm run test:php:watch:文件变更自动重跑(类似 Jest watch);npm run lint:php:独立做 PHP 代码规范检查。
官方文档同时提示:这些 phpunit 命令要求 wp-env 正在运行且 composer 依赖已安装;npm 脚本会自动为你启动 wp-env(对应test:unit:php:setup),而 php.md 之所以要求先手动status/start,是为了让 Agent 对环境的生命周期有显式控制、避免反复启停。
从 wp-env 视角理解 PHPUnit 的执行位置
一个值得注意的实现细节:test:unit:php:base使用wp-env ... run --env-cwd='wp-content/plugins/gutenberg' wordpress vendor/bin/phpunit,这意味着 PHPUnit 是在wp-env 容器内部的 WordPress 环境中执行的,工作目录是插件挂载点wp-content/plugins/gutenberg。这正好呼应了 bootstrap.php 中"如果运行在 WP 的 build 目录中则声明WP_RUN_CORE_TESTS"(第 31-33 行)的逻辑,也解释了为什么vendor/bin/phpunit必须在容器内可访问——Composer 依赖安装于插件根目录。
在 CI 侧,.github/workflows/unit-test.yml 展示了同样的模式:先运行npm run wp-env-test -- run wordpress ... vendor/bin/phpunit -- --version校验 PHPUnit 可用,再分别执行npm run test:unit:php(单站点)与npm run test:unit:php:multisite(多站点),最后通过解析输出中的OK (N tests或Tests: N, Assertions:来判定通过与否。
测试目录结构与命名约定
Gutenberg 的 PHP 测试集中在 phpunit/ 目录,其组织与 phpunit.xml.dist 的 testsuite 声明一一对应:
<testsuites> <testsuite name="default"> <directory suffix="-test.php">./phpunit/</directory> <directory suffix=".php">./phpunit/tests/</directory> <directory suffix=".php">./phpunit/blocks/</directory> </testsuite> </testsuites>关键约定:
- 根级测试文件(如
phpunit/class-wp-theme-json-test.php、phpunit/class-wp-duotone-test.php)以-test.php结尾; - 子目录测试(
phpunit/tests/、phpunit/blocks/)以.php结尾即被收集; - 组排除:
ms-required(仅多站点可运行)与fontsapi(字体 API 实验特性)两个组在单站点运行时被排除,避免环境不匹配导致误报。
相应地,phpunit/multisite.xml 是面向 WordPress 多站点(Multisite)的独立配置:它通过<env name="WP_MULTISITE" value="1" />开启多站点模式,排除ms-excluded与fontsapi组。npm 侧对应的命令是npm run test:unit:php:multisite(见 package.json)。单站点与多站点是两套独立运行通道,改动涉及多站点行为(如is_multisite()分支、grant_super_admin等)时必须同时跑两套。
一个真实的测试文件解剖
以 phpunit/block-supports/anchor-test.php 为例,可以看到仓库 PHP 测试的典型写法:
/** * @covers ::gutenberg_register_anchor_support * @dataProvider data_gutenberg_register_anchor_support */ public function test_gutenberg_register_anchor_support( bool $support, ?array $value, array $expected ) { // ... gutenberg_register_anchor_support( $block_type ); } public function data_gutenberg_register_anchor_support(): array { // 返回多组 (support 配置, 属性值, 期望输出) 组合 }要点:
- 测试类继承
WP_UnitTestCase(如 phpunit/class-wp-theme-json-test.php),获得 WordPress 测试工厂(self::factory()->user->create()等)与完整事务回滚; - 大量使用
@dataProvider做参数化测试,覆盖边界值与多分支; - 使用
@covers注解标明被测函数/类,便于生成覆盖率报告。
核心约束:测试"内置(带前缀)函数"而非源码函数
php.md 将 Testing prefixed functions 列为重点深读内容,并提示前缀规则本身的完整描述位于根目录 AGENTS.md 的 pitfalls 中。这是 Gutenberg PHP 测试最容易踩坑、也最具仓库特色的规则。
前缀机制的原理
Gutenberg 插件的 PHP 代码在构建时会被自动改名,以避免与 WordPress Core 的函数/类重名冲突。相关机制详见 docs/contributors/code/build-system-function-prefixing.md:
| 符号类型 | 源码写法(开发时) | 构建后(运行时实际存在) |
|---|---|---|
| 函数名 | block_core_navigation_link_build_css_colors() | gutenberg_block_core_navigation_link_build_css_colors() |
| 函数调用 | wp_get_typography_font_size_value() | gutenberg_get_typography_font_size_value() |
| 类名 | WP_Style_Engine | WP_Style_Engine_Gutenberg |
构建产物输出到build/目录,lib/blocks.php按优先级加载这些内置文件。因此运行时真正被调用的是带前缀的名字,测试也就必须调用这些带前缀的名字,否则会触发"函数不存在"错误或测试到错误的代码版本。
测试必须调用内置名称
Testing Prefixed Functions 给出了明确示例(测试文件位于phpunit/blocks/):
class My_Block_Test extends WP_UnitTestCase { public function test_my_function() { // 测试内置函数(带 gutenberg_ 前缀) $result = gutenberg_block_core_my_block_render_function( $args ); $this->assertEquals( $expected, $result ); } public function test_my_class() { // 测试内置类(带 _Gutenberg 后缀) $handler = new WP_Example_Block_Handler_Gutenberg(); $result = $handler->process( $input ); $this->assertEquals( $expected, $result ); } }仓库中这样的例子俯拾皆是。例如 phpunit/block-supports/anchor-test.php 直接调用gutenberg_register_anchor_support()与gutenberg_apply_anchor_support();phpunit/block-supports/block-style-variations-test.php 则实例化WP_Theme_JSON_Resolver_Gutenberg、WP_Theme_JSON_Gutenberg等_Gutenberg后缀类;phpunit/class-wp-theme-json-test.php 在类注释中直接以@covers WP_Theme_JSON_Gutenberg声明被测类。
反向场景:回迁到 WordPress Core 时
文档同时给出了一条重要例外:如果测试被回迁(backport)到 WordPress Core,则必须改回测试无前缀版本(block_core_my_block_render_function、WP_Example_Block_Handler)。这正是前缀机制设计目标之一——Gutenberg 可以独立于 Core 发布周期演进,而代码回迁后只需去掉前缀即可复用。
Agent 测试写作规范(通用红线)
虽然 php.md 本身很短,但它隶属于.agents/skills/testing/SKILL.md技能体系,其中的通用规则对 PHP 测试同样适用,且与 PHP 代码评审(phpcs)直接相关:
- 先与作者确认测试清单:动笔前先拟出测试用例名(从用户视角描述行为,一条用例对应一个行为),与作者确认后再写测试体;无人值守时把清单写进总结供评审。对应文档规范见 Testing Overview 的 "Describing tests"。
- 禁止通过削弱测试来让失败变绿:不放松断言、不无端增加等待/超时、不静默跳过用例。要么诊断根因,要么如实报告失败。
- 禁止为了修测试而改生产代码,除非生产代码本身就是 Bug 源:测试通过不是任务完成的最终标准,验证生产代码按预期工作才是核心目标。
- 代码规范同样适用:Gutenberg 的 PHP 测试代码与生产代码同等对待(testing-overview.md 明确"Tests are also part of our code base")。仓库提供了专用的编码标准包 test/php/gutenberg-coding-standards(作为 path 仓库被 composer.json 引用为
gutenberg/gutenberg-coding-standards),配合wp-coding-standards/wpcs、phpcompatibility/phpcompatibility-wp等依赖,通过vendor/bin/phpcs/vendor/bin/phpcbf强制执行。
快速上手清单(Agent 与开发者通用)
综合 php.md 与仓库配置,一次规范的 PHP 测试会话可以归纳为以下步骤:
- 准备依赖:
npm install && composer install(安装 Node 与 PHP 依赖,含 PHPUnit、PHP_CodeSniffer、编码标准包)。 - 检查环境:
npm run wp-env-test status;若未运行则npm run wp-env-test start。 - 运行测试:
- 全部 PHP 测试(仅 PHPUnit):
composer test; - 指定文件/目录:
vendor/bin/phpunit phpunit/block-supports/; - 全链路(lint + 单测):
npm run test:php; - 仅单测:
npm run test:unit:php; - 多站点:
npm run test:unit:php:multisite; - 监听模式:
npm run test:php:watch。
- 全部 PHP 测试(仅 PHPUnit):
- 遵守符号约定:测试中一律调用构建后的
gutenberg_*函数与*_Gutenberg类;若面向 Core 回迁则使用无前缀版本。 - 风格检查:
composer run lint(检查)与composer run format(自动修复),或直接vendor/bin/phpcs/vendor/bin/phpcbf。 - CI 对齐:本地应同时通过单站点与多站点两套 PHPUnit(对应 .github/workflows/unit-test.yml 的 CI 流水线),并保持 PHP 代码符合 WordPress Coding Standards。
总结
.agents/skills/testing/references/php.md用极简篇幅划定了 Gutenberg 仓库 PHP 测试的关键路径:wp-env 测试环境先行、composer test/vendor/bin/phpunit执行、前缀函数测试约定、以及指向 Testing Overview 的深度文档路由。其背后是仓库完整的工程化支撑——phpunit.xml.dist的测试套件编排、bootstrap.php的 WordPress 测试环境引导、composer/npm 双通道命令封装、以及构建期函数前缀机制。对 Agent 而言,这些约定确保了测试行为的确定性(不反复启停环境、不改弱断言、不误改生产代码);对开发者而言,它们就是一份可直接落地的 PHPUnit 实战手册。
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考