Writing Behat Tests for WP-CLI

WP-CLI 使用 Behat 进行功能测试。本指南介绍如何为 WP-CLI 命令和包编写、运行 Behat 测试。WP-CLI 是 WordPress 的命令行接口,可通过程序执行管理和开发任务;项目地址为 wp-cli.org 和 WordPress CLI 团队页面。

简介

什么是 Behat 测试?

Behat 是 PHP 的行为驱动开发(BDD)框架。PHP 是广泛使用的开源通用脚本语言,尤其适合 Web 开发,可嵌入 HTML;名称是 PHP: Hypertext Preprocessor 的递归缩写,参阅 PHP 手册。在 WP-CLI 中,Behat 测试作为功能测试执行整个命令,验证其行为是否符合预期。单元测试隔离检查单个函数;功能测试验证命令从头到尾是否正确运行。

为什么 Behat 测试很重要

保持版本之间的稳定性,是 WP-CLI 对用户的重要承诺。Behat 测试帮助确保:

  • 命令与文档描述一致。
  • 修改不会破坏现有功能。
  • 边界情况得到正确处理。
  • 完整的命令执行流程得到验证。

大多数拉取请求都需要测试。新增功能需要测试;修复缺陷也需要测试,以防回归。

开始使用

每个包含命令的 WP-CLI 仓库都有 features/ 目录,其中有一个或多个 *.feature 文件。原文称其为 YAML 格式;这些文件实际采用下文展示的 Gherkin 语法。

编写第一个测试

基本测试结构

下面是一个简单示例:

Feature: Manage WordPress options

  Scenario: Read an individual option
    Given a WP install

    When I run `wp option get home`
    Then STDOUT should be:
      """
      https://example.com
      """

这个测试采用 Gherkin 语法,包含三个关键步骤组成部分:

  • Feature::记录文件的范围,应描述待测的整体功能。
  • Scenario::描述具体测试用例。每个 feature 文件可以包含多个场景。
  • Given:准备测试初始环境,即前置条件。
  • When:触发事件,即待测动作。
  • Then:断言事件完成后的预期结果,即验证。

用更自然的语言描述就是:

已有一个 WordPress 安装实例。当运行 wp option get home 时,命令输出应为“https://example.com”。

第一个测试

为一个输出问候语的假想命令编写测试:

  1. 创建 features/greeting.feature 文件:
Feature: Test greeting command

  Scenario: Display a greeting
    Given an empty directory

    When I run `wp greeting hello`
    Then STDOUT should contain:
      """
      Hello
      """
    And the return code should be 0

此测试:
1. 从空目录开始。
2. 运行命令。
3. 验证输出包含“Hello”。
4. 验证命令成功,退出码为 0。

常用步骤定义

WP-CLI 提供许多内置步骤定义,以下是最常用的一些:

Given:准备环境

  • Given an empty directory:创建干净的工作目录。
  • Given a WP install:安装一个全新的 WordPress 实例。
  • Given a WP multisite install:安装多站点模式的 WordPress。Multisite 可在一次 WordPress 安装中创建站点网络,自 3.0 起提供,延续了已停止的 WPMU(WordPress Multiuser)项目,其功能已并入核心。参阅高级管理手册:创建站点网络。
  • Given a database:创建空数据库。

When:执行动作

  • When I run 'wp command':执行命令,并预期成功。
  • When I try 'wp command':执行命令,允许成功或失败。
  • When I launch in the background 'wp command':在后台运行命令。

Then:断言

  • Then STDOUT should be::输出精确匹配。
  • Then STDOUT should contain::输出包含指定文本。
  • Then STDOUT should not contain::输出不包含指定文本。
  • Then the return code should be 0:命令执行成功。
  • Then the return code should be 1:命令执行失败。
  • Then STDOUT should be empty:没有输出。

全部可用步骤见 Behat 步骤参考。

运行测试

配置测试环境

运行测试前,需要准备测试数据库。composer behat 脚本会自动检测数据库是否可用;没有正在运行的数据库时回退到 SQLite,便于零配置开始。

有两种选择:

方案一:SQLite(简便起见推荐)

最简单的方法是使用 SQLite,无需配置数据库:

WP_CLI_TEST_DBTYPE=sqlite composer behat

也可直接运行 composer behat,未配置数据库时由它自动选择 SQLite。

方案二:MySQL/MariaDB。MySQL 是关系型数据库管理系统,数据库用于结构化保存内容、配置和其他选项;参阅 MySQL 官网。

若希望使用 MySQL 或 MariaDB,运行准备脚本创建测试数据库:

composer prepare-tests

这会创建 MySQL 用户 wp_cli_test,密码为 password1,并授予其对 wp_cli_test 数据库的全部权限。

注意:MySQL 8.0 更改了默认认证插件。遇到连接问题时,参阅这篇博文。原页的术语提示也解释了 WordPress 插件:它们是以 PHP 编写、与 WordPress 集成、用于扩展网站功能的软件,可以来自免费插件目录,也可以是第三方付费插件;这里的数据库认证插件与 WordPress 插件不是同一概念。

运行测试套件

运行全部测试:

composer behat

运行某个 feature 文件的测试:

composer behat -- features/option.feature

按行号运行特定场景:

composer behat -- features/option.feature:10

显示详细输出:

composer behat -- features/option.feature --format pretty

仅重新运行失败的测试:

composer behat-rerun

查找可用步骤

查看所有可用步骤定义:

composer behat -- --definitions l

编写测试时,这有助于找到适用的步骤。

进阶用法

编写有效的测试

每次测试一件事

每个场景应只测试一个功能点:

# Good - tests one specific behavior
Scenario: Delete a post
  Given a WP install
  And I run `wp post create --post_title='Test' --porcelain`
  And save STDOUT as {POST_ID}

  When I run `wp post delete {POST_ID}`
  Then STDOUT should contain:
    """
    Success: Trashed post
    """

# Bad - tests multiple unrelated behaviors
Scenario: Create, update, and delete a post
  # Too much in one test...

使用描述性的场景名称

清楚说明测试对象:

# Good
Scenario: Fail gracefully when post doesn't exist

# Bad
Scenario: Test delete

同时测试成功和失败

不要只测试顺利成功的路径:

Scenario: Successfully create a post
  Given a WP install
  When I run `wp post create --post_title='Test'`
  Then the return code should be 0

Scenario: Fail when required argument is missing
  Given a WP install
  When I try `wp post create`
  Then the return code should be 1
  And STDERR should contain:
    """
    Error
    """

使用变量

把命令输出保存到变量中,供后续使用:

Scenario: Create and then fetch a post
  Given a WP install

  When I run `wp post create --post_title='Test Post' --porcelain`
  Then save STDOUT as {POST_ID}

  When I run `wp post get {POST_ID} --field=title`
  Then STDOUT should be:
    """
    Test Post
    """

测试表格和结构化输出

测试表格输出

Scenario: List posts in table format
  Given a WP install
  And I run `wp post create --post_title='First'`
  And I run `wp post create --post_title='Second'`

  When I run `wp post list --fields=ID,post_title`
  Then STDOUT should be a table containing rows:
    | ID | post_title |
    | 1  | First      |
    | 2  | Second     |

测试 JSON 输出

Scenario: List posts in JSON format
  Given a WP install
  And I run `wp post create --post_title='Test' --porcelain`

  When I run `wp post list --format=json`
  Then STDOUT should be JSON containing:
    """
    [{"post_title":"Test"}]
    """

测试 CSV 输出

Scenario: Export posts as CSV
  Given a WP install

  When I run `wp post list --format=csv`
  Then STDOUT should be CSV containing:
    | ID | post_title | post_status |
    | 1  | Test       | publish     |

测试文件和目录

Scenario: Create a plugin file
  Given a WP install

  When I run `wp scaffold plugin my-plugin`
  Then the my-plugin/my-plugin.php file should exist
  And the my-plugin/my-plugin.php file should contain:
    """
    Plugin Name: My Plugin
    """

Background 段

使用 Background: 为所有场景执行共同的准备步骤:

Feature: Post management

  Background:
    Given a WP install
    And I run `wp post create --post_title='Test'`

  Scenario: Update post title
    When I run `wp post update 1 --post_title='Updated'`
    Then STDOUT should contain:
      """
      Success
      """

  Scenario: Delete post
    When I run `wp post delete 1`
    Then STDOUT should contain:
      """
      Success
      """

测试错误消息

使用 When I try 替代 When I run,允许命令失败:

Scenario: Error when plugin doesn't exist
  Given a WP install

  When I try `wp plugin activate non-existent-plugin`
  Then the return code should be 1
  And STDERR should contain:
    """
    Error: The 'non-existent-plugin' plugin could not be found.
    """

使用 Scenario Outline

用一个场景测试多组输入:

Scenario Outline: Create posts with different titles
  Given a WP install

  When I run `wp post create --post_title='<title>'`
  Then STDOUT should contain:
    """
    Success
    """

  Examples:
    | title          |
    | Simple Title   |
    | Title's Quotes |
    | UTF-8: 日本語   |

编写自定义步骤

高级用例可能需要自定义步骤定义。这些定义使用 PHP 编写,放在包的测试引导文件中。

自定义步骤定义示例:

/**
 * @Given a custom configuration file
 */
public function aCustomConfigurationFile() {
    $config = <<<EOT
custom_setting: value
another_setting: 123
EOT;
    $this->proc( "echo '{$config}' > wp-cli.yml" )->run_check();
}

自定义步骤应:
– 使用有描述性的文档块注解。
– 遵循 Gherkin 的 Given/When/Then 约定。
– 可在多个测试中复用。
– 使用已有的 WP-CLI 测试框架方法。

对大多数包来说,wp-cli/wp-cli-tests 的内置步骤已经够用。只有必要时才创建自定义步骤。

使用不同 PHP 和 WordPress 版本测试

WP-CLI 在 CI 中针对多个 PHP 和 WordPress 版本运行测试。你的测试应在这些版本上均可工作。

需要注意:
– 不同 WordPress 版本的行为可能不同。
– 使用与版本相适应的预期结果。
– 避免测试 WordPress 核心自身的缺陷,应测试你的命令。核心是运行 WordPress 所需的软件集合,由核心开发团队构建。

数据库凭据

MySQL/MariaDB 测试默认使用:
– 数据库:wp_cli_test
– 用户名:wp_cli_test
– 密码:password1
– 主机:localhost

可用 wp-cli/wp-cli-tests 提供的环境变量覆盖:

  • WP_CLI_TEST_DBNAME
  • WP_CLI_TEST_DBUSER
  • WP_CLI_TEST_DBPASS
  • WP_CLI_TEST_DBHOST

也可设置 WP_CLI_TEST_DBTYPE=sqlite,使用 SQLite,完全避开数据库配置。

最佳实践

建议这样做

  • 测试命令行为,而非实现。验证命令做什么,而非具体怎么做。
  • 让测试相互独立。每个测试都应能单独成功运行。
  • 使用有意义的测试数据。例如贴近实际的文章标题、用户名等。
  • 测试边界情况。例如空字符串、特殊字符和大数值。
  • 保持场景专注。每个场景只包含一个逻辑测试。
  • 用 Given 准备环境。不要混合准备工作和测试动作。
  • 测试错误情况。验证命令是否按预期失败。

避免这样做

  • 不要测试 WordPress 核心功能。测试的是你的命令。
  • 不要让测试互相依赖。每个场景都应独立。
  • 不要硬编码 ID。使用变量或 porcelain 输出。
  • 不要跳过错误测试。命令失败的情况同样重要。
  • 不要测试实现细节。专注于用户可见行为。
  • 不要忘记测试帮助文本。加入 wp help your-command 的场景。

为新包生成测试框架

创建新 WP-CLI 包时,用 wp scaffold package-tests 生成测试基础设施:

wp scaffold package my-package
cd my-package
wp scaffold package-tests .

这会创建:

  • .github/workflows/testing.yml:GitHub Actions 工作流。原页术语提示将 GitHub 解释为在线托管 Git 仓库、供开发者分享、复制和修改代码的网站,并介绍了拉取请求:分支修改可在合并前接受审核讨论。原提示关于私有仓库须付费的说法带有历史性,不能据此判断当前套餐。
  • features/:存放 feature 文件的目录。
  • features/load-wp-cli.feature:验证 WP-CLI 能加载的基础测试。
  • 其他测试基础设施文件。

详情见 scaffold-package-command 文档。

故障排查

测试无法连接数据库

遇到数据库连接错误时:

  1. 尝试改用 SQLite:WP_CLI_TEST_DBTYPE=sqlite composer behat。
  2. 如果使用 MySQL,确认其正在运行:mysql -u wp_cli_test -ppassword1 wp_cli_test。
  3. 检查 MySQL 版本;MySQL 8.0 更改了认证方式。
  4. 确认数据库已创建:composer prepare-tests。

本地通过,CI 失败

常见原因:

  • PHP 版本不同,检查 CI 矩阵。
  • WordPress 版本不同,检查测试矩阵。
  • 时序问题,有些命令需要更长时间。
  • 数据库排序规则不同。

找不到命令

测试期间找不到命令时:

  1. 确认 composer.json 正确声明了命令。
  2. 确认 composer install 成功运行。
  3. 确认命令类已正确自动加载。

测试缓慢

测试太慢时:

  • 尽量减少数据库操作。
  • 可以时用 Given an empty directory 替代 Given a WP install。
  • 避免不必要的插件安装。
  • 用 --porcelain 减少输出解析。

更多资源

WP-CLI 包中的实例

来自 WP-CLI 核心包的真实示例:

研究这些示例,了解各种命令在实践中如何测试。


现在可以为自己的 WP-CLI 命令编写全面的 Behat 测试了。好的测试会让 WP-CLI 对所有用户更加可靠。

WP-CLI post get 官方命令参考

Gherkin 语法参考


来源与版权

来源:WordPress.org WP-CLI Handbook,WP-CLI 贡献者。《Writing Behat Tests for WP-CLI》中文译稿,依据用户已取得的转载授权,保留完整原示例。 阅读原文。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容