ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WP-CLI 仓库开发与测试指南:从 AGENTS.md 到 Behat 行为驱动测试实战

WP-CLI 仓库开发与测试指南:从 AGENTS.md 到 Behat 行为驱动测试实战
  • CLI
  • 开发工具
  • 后端

【免费下载链接】wp-cli

⚙️ WP-CLI framework

项目地址:https://gitcode.com/gh_mirrors/wp/wp-cli
点击查看免费下载

WP-CLI 是 WordPress 的官方命令行工具,本仓库(wp-cli/wp-cli)是其框架核心源码。本文以仓库根目录的 AGENTS.md 为骨架,系统讲解向该仓库提交代码时的开发规范、构建与测试命令、编码风格约束,以及基于 Behat 的行为驱动测试(BDD)体系与全部自定义步骤定义,帮助读者掌握 WP-CLI 框架的完整开发与验证流程。

一、仓库定位:一份给 Agent 与开发者的开发指南

AGENTS.md 是 WP-CLI 框架仓库的“开发代理指南”,开篇即说明:本包是 WP-CLI(WordPress 官方命令行接口)的组成部分,关于项目结构与开发工作流的详细解释请参阅主 README.md。

在动手写代码之前,文档要求开发者先做四件事:

  1. 遵循现有约定:分析现有代码库,理解编码风格、命名约定与架构模式;
  2. 聚焦本包范围:所有改动必须与本包功能相关,不得发散到无关模块;
  3. 编写测试:所有新功能与缺陷修复必须附带 Behat 验收测试(位于features/目录),同时tests/目录下也可能存在 PHPUnit 单元测试;
  4. 更新文档:若改动影响用户可见功能,需同步更新对应的行内代码文档。

其中第 3 点是本项目最鲜明的特色:WP-CLI 框架的测试以行为驱动测试为主,全部功能验收都沉淀在features/目录下的大量.feature文件中(如 features/cli.feature、features/command.feature、features/runner.feature 等),这是后续章节的重点。

二、构建与运行:最小化检查矩阵

AGENTS.md 给出了一套“改动类型 → 最小检查命令”的对应关系,这是日常开发中最实用的速查表:

你改了什么需要运行
任意 PHP 文件composer phpcs -- <file>与composer phpstan
有单元测试覆盖的逻辑composer phpunit -- --filter <TestName>
单个 feature 文件composer behat -- features/<name>.feature
单个场景(Scenario)composer behat -- features/<name>.feature:<line>

核心思想是“只跑覆盖你改动的最窄检查”,避免无谓的耗时。例如只改了一个 PHP 文件,就先做代码风格与静态分析;改动了被单元测试覆盖的逻辑,就用--filter精确到某个测试类/方法。

失败后的重跑策略

  • composer behat-rerun:只重新运行上次失败的场景;
  • composer behat -- --stop-on-failure:遇到第一个失败即停止,而不是跑完所有场景。

这两条命令对应 composer.json 中的behat-rerun与behat脚本,它们在长时间运行的 Behat 套件下能大幅缩短迭代周期。

全量测试composer test:谨慎运行

composer test会运行全部套件,包括整个 Behat 套件。文档特别提醒:

  • 在大多数包中,这意味着每个场景都要从零安装一次 WordPress,整个流程需要数十分钟;
  • 需要jq、MySQL/MariaDB 客户端以及预先准备好的测试数据库;
  • 需要访问 WordPress.org 的网络权限(下载安装包)。

因此它只适合在改动涉及跨模块(cross-cutting)内容、准备开 Pull Request 之前运行;不要为了验证两行改动去跑全量测试。在 composer.json 中可以看到test脚本由@lint、@lint-gherkin、@phpcs、@phpstan、@phpunit、@behat六个子任务按顺序串联而成。

环境变量:让工具输出干净可读

在运行上述命令前,建议先设置两个环境变量,让工具以紧凑的纯文本方式报告问题,而不是把进度条和 ANSI 颜色画进你回读的输出里:

export NO_COLOR=1 export WP_CLI_TEST_QUIET=1

绿色 ≠ 通过 CI

文档明确提示:本地composer test全绿不等于CI 全绿。actionlint(GitHub Actions 工作流检查)与typos(拼写检查)也在每次 Pull Request 上运行,但它们并不属于composer test的一部分。这意味着提交前还应对照 CI 配置自查这两项。

三、Composer 命令速查

项目使用 Composer 管理依赖并驱动脚本(脚本定义见 composer.json 的scripts段),常用命令如下:

命令作用
composer install安装依赖
composer test运行完整测试套件:lint、gherkin 风格检查、代码风格、静态分析、单元测试与行为测试
composer lint检查语法错误
composer lint-gherkin检查 Behat feature 文件的风格规范
composer phpcs检查代码风格违规
composer phpcbf自动修复代码风格违规
composer phpstan运行静态分析
composer phpunit运行单元测试
composer behat运行行为驱动测试

值得注意的是,phpcs、phpstan、phpunit、behat等脚本在 composer.json 中实际委托给wp-cli/wp-cli-tests包提供的工具函数(run-phpcs-tests、run-phpstan-tests、run-php-unit-tests、run-behat-tests等),因此运行composer test前需先composer install并通过 composer.json 的require-dev安装好wp-cli/wp-cli-tests。

四、编码规范:WP_CLI_CS 与 PHP_CodeSniffer

项目遵循WP_CLI_CS编码标准,由 PHP_CodeSniffer 强制执行,配置位于 phpcs.xml.dist。提交代码前需运行:

composer phpcs # 检查违规 composer phpcbf # 自动修复违规

从 phpcs.xml.dist 源码可以看到该规则的几个关键细节:

  • 基于WP_CLI_CS规则集,仅放行while控制结构中的赋值表达式(Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition);
  • testVersion设置为7.2-,即通过 PHPCompatibility 检查代码对 PHP 7.2 及以上版本的兼容性(与 composer.json 中php: >=7.2.24 || ^8.0的要求一致);
  • 使用WordPress.NamingConventions.PrefixAllGlobals要求全局命名空间中的类、函数均以前缀命名,并为钩子函数额外放行cli_前缀;
  • 对tests/data/、bundle/等目录整体排除检查。

行内文档原则

文档要求“尽量只写高价值注释”,不要通过注释“跟用户对话”——即注释应解释代码意图而非复述显而易见的逻辑。

五、测试体系总览

项目拥有包含单元测试、行为测试与静态分析在内的完整测试体系,全部测试会在每次 Pull Request 时由 GitHub Actions 运行。

测试类型框架位置配置
单元测试PHPUnittests/phpunit.xml.dist
行为驱动测试Behatfeatures/behat.yml
静态分析PHPStan—phpstan.neon.dist

单元测试(PHPUnit)

单元测试位于tests/目录,配置在 phpunit.xml.dist:引导文件为tests/bootstrap.php,测试套件扫描tests目录下所有*Test.php文件,并将php/目录纳入覆盖率统计。

从 tests/bootstrap.php 可以看到引导流程:定义WP_CLI_ROOT、加载 Composer 自动加载器、引入php/utils.php与php/dispatcher.php、注册bundle/rmccue/requests的 Requests 自动加载器,并加载测试专用的wpdb.php与 mock HTTP 传输层。

phpunit.xml.dist 还开启了严格的失败语义:convertErrorsToExceptions、convertWarningsToExceptions、convertNoticesToExceptions、convertDeprecationsToExceptions均为true,任何级别的 PHP 告警都会让测试失败。

编写单元测试的约定:继承基类WP_CLI\Tests\TestCase。例如 tests/UtilsTest.php 中的class UtilsTest extends TestCase,并通过set_up_before_class()调用WP_CLI::get_runner()->init_config()完成运行器配置初始化。同类测试还包括 tests/RunnerTest.php、tests/FormatterTest.php、tests/CommandFactoryTest.php 等。

行为驱动测试(Behat)

行为测试用 Behat 编写,配置在 behat.yml:默认套件使用WP_CLI\Tests\Context\FeatureContext上下文,扫描features目录。

从 features/command.feature 可以看到典型场景——用Given an empty directory准备环境,用When I run \wp help --help`执行命令,再用Then STDOUT should contain:` 断言输出:

Scenario: Registered WP-CLI commands Given an empty directory When I run `wp help --help` Then STDOUT should contain: """ wp help [<command>...] """

features/runner.feature 则展示了wp-cli.yml配置与--path参数处理的验证,而 features/utils.feature 演示了When I try(期望失败输出)、Then STDERR should contain、Then the return code should be 1的组合用法:

Scenario Outline: Check that `proc_open()` and `proc_close()` aren't disabled for `Utils\run_mysql_command()` When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--ddisable_functions=<func>} --skip-wordpress eval "WP_CLI\Utils\run_mysql_command( null, array() );"` Then STDERR should contain: """ Error: Cannot do 'run_mysql_command': The PHP functions `proc_open()` and/or `proc_close()` are disabled """ And STDOUT should be empty And the return code should be 1

注意:如果测试中预期命令产生错误输出,必须使用When I try ...而不是When I run ...,否则 Behat 会直接把错误当作运行失败处理。

静态分析(PHPStan)

phpstan.neon.dist 显示该项目在level 9(最高严格级别)下扫描php/commands、php/WP_CLI、php/*.php与tests目录,启用 bleedingEdge 配置与 strictRules(uselessCast、closureUsesThis等),并通过scanDirectories把bundle/rmccue/requests与vendor/wp-cli/wp-cli-tests纳入分析范围。

六、Behat 自定义步骤定义全解

WP-CLI 在 Behat 之上封装了一套自定义步骤定义(step definitions),用于编写 feature 测试。下文按 Given / When / Then 三类完整列出。

Given 步骤(准备阶段)

步骤作用
Given an empty directory创建空目录
Given an empty|non-existent <name> directory创建或删除指定目录
Given an empty cache清空 WP-CLI 缓存目录
Given a <name> file:/Given a <name> cache file:以给定内容创建文件
Given "<a>" replaced with "<b>" in the <name> file用正则表达式对文件做查找替换
Given that HTTP requests to <url> will respond with:模拟对指定 URL 的 HTTP 请求响应(由 tests/includes/mock-requests-transport.php 提供底层支持)
Given WP files下载 WordPress 文件但不安装
Given wp-config.php通过wp config create生成 wp-config.php
Given a database创建空数据库
Given a WP install(ation)安装 WordPress
Given a WP install(ation) in :subdir在指定子目录安装 WordPress
Given a WP install(ation) with Composer用 Composer 安装 WordPress
Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory用 Composer 安装 WordPress 并指定自定义 vendor 目录
Given a WP multisite (subdirectory\|subdomain)? install(ation)安装 WordPress Multisite
Given these installed and active plugins:安装并激活一个或多个插件
Given a custom wp-content directory配置自定义wp-content目录
Given download:将多个文件下载到指定位置
Given save (STDOUT\|STDERR) <contents>? as {<var>}将 STDOUT 或 STDERR 内容存入变量
Given a new Phar with (the same version\|version "<ver>")以指定版本构建新的 WP-CLI Phar 文件
Given a downloaded Phar with (the same version\|version "<ver>")从 GitHub 下载指定版本的 WP-CLI Phar
Given save the <name> file <contents>? as {<var>}将指定文件内容存入变量
Given a misconfigured WP_CONTENT_DIR constant directory修改 wp-config.php 将WP_CONTENT_DIR设为空字符串
Given a dependency on current wp-cli将wp-cli/wp-cli添加为 Composer 依赖
Given a PHP built-in web server在当前目录启动 PHP 内置 Web 服务器
Given a PHP built-in web server to serve :subdir在指定子目录启动 PHP 内置 Web 服务器

When 步骤(执行阶段)

步骤作用
When I launch in the background \``后台启动给定命令
When I (run\|try) \`| 运行或尝试运行给定命令(try` 用于预期失败场景)
When I (run\|try) \` from ''`在指定子目录中运行或尝试运行命令
When I (run\|try) the previous command again再次运行或尝试上一条命令

Then 步骤(断言阶段)

步骤作用
Then the return code should( not)? be <n>断言上一条命令的退出码
Then (STDOUT\|STDERR) should( strictly)? (be\|contain\|not contain):断言 STDOUT/STDERR 的内容(strictly表示严格比较)
Then (STDOUT\|STDERR) should be a number断言输出为数字
Then (STDOUT\|STDERR) should not be a number断言输出非数字
Then STDOUT should be a table containing rows:断言 STDOUT 为包含给定行的表格
Then STDOUT should end with a table containing rows:断言 STDOUT 以包含给定行的表格结尾
Then STDOUT should be JSON containing:断言 STDOUT 为合法 JSON
Then STDOUT should be a JSON array containing:断言 STDOUT 为合法 JSON 数组
Then STDOUT should be CSV containing:断言 STDOUT 为包含特定值的 CSV
Then STDOUT should be YAML containing:断言 STDOUT 为包含特定内容的 YAML
Then (STDOUT\|STDERR) should be empty断言输出为空
Then (STDOUT\|STDERR) should not be empty断言输出非空
Then (STDOUT\|STDERR) should be a version string (<\|<=\|>\|>=\|==\|=\|<>) <ver>断言输出为与给定版本比较的版本字符串
Then the <name> (file\|directory) should( strictly)? (exist\|not exist\|be:\|contain:\|not contain):断言文件/目录(不)存在或包含特定内容
Then the contents of the <name> file should( not)? match <regex>用正则匹配文件内容
Then (STDOUT\|STDERR) should( not)? match <regex>用正则匹配 STDOUT/STDERR
Then an email should (be sent\|not be sent)断言(未)发送邮件
Then the HTTP status code should be :code断言访问http://localhost:8080的 HTTP 状态码

这些步骤与 features/ 目录下数十个 feature 文件一一对应,例如 features/help.feature 大量使用Then STDOUT should contain,features/validation.feature 使用Then the return code should be验证参数校验,features/context.feature 使用Given a WP install(ation)准备运行上下文。读者在新增场景时,应先在这些既有文件中寻找相近的模式进行模仿,保持风格一致。

七、测试编写约定与提交前自检清单

测试约定

  • 先看既有测试:新增测试前先阅读现有测试,遵循既定模式;
  • 单元测试:继承基类WP_CLI\Tests\TestCase;
  • Behat 测试:只能运行 composer.json 中安装的 WP-CLI 命令(require-dev中声明的wp-cli/db-command、wp-cli/entity-command、wp-cli/extension-command等命令包才可用)。

提交前自检清单

  1. 改动 PHP 文件 → 运行composer phpcs -- <file>与composer phpstan;
  2. 改动有单测覆盖的逻辑 → 运行composer phpunit -- --filter <TestName>;
  3. 修改/新增 feature 文件 → 运行composer behat -- features/<name>.feature,失败后可用composer behat-rerun精准重跑;
  4. 涉及跨模块改动 → 设置NO_COLOR=1与WP_CLI_TEST_QUIET=1后运行composer test;
  5. 额外检查 CI 中会运行、但本地composer test不覆盖的actionlint与typos。

文档同步

AGENTS.md 还提醒:README.md 可能是通过wp scaffold package-readme从代码库动态生成的。若改动影响用户可见行为,需要修改代码库中对应的文档源(如命令的 PHPdoc),而非直接手改 README;同时遵循“只写高价值注释”的行内文档原则。

通过以上流程,开发者可以在保持 WP-CLI 框架高质量标准的前提下,快速、精准地完成代码贡献的验证闭环——从最小化检查到全量测试,从编码规范到行为驱动测试步骤的熟练运用。

  • CLI
  • 开发工具
  • 后端

【免费下载链接】wp-cli

⚙️ WP-CLI framework

项目地址:https://gitcode.com/gh_mirrors/wp/wp-cli
点击查看免费下载

相关推荐

上一篇:Sophia Script for Windows 技术指南:用 150+ 个 PowerShell 函数精细调校 Windows 10/11
下一篇:全网无损音乐一个播放器搞定:lxmusic开源音源库保姆级接入指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进