原文:How to add automated unit tests to your WordPress plugin
作者:David Perez
原文发布日期:2025 年 12 月 16 日
WordPress 插件会面对不同的 WordPress 版本、PHP 配置、主题和插件组合。代码在开发者自己的机器上正常,不代表每位用户的站点都没有问题;后续改动也可能意外破坏原有功能。把测试纳入开发流程,能在发布前发现回归,并让每次改动都有可重复的检查依据。
本文沿用 David Perez 的 Composer 路线,说明如何建立 WordPress 集成测试、使用 PHPUnit 编写断言,再把测试接入 GitHub Actions。测试不可能覆盖所有生产环境组合,但可以稳定检查关键函数、钩子和内容类型在预期条件下的行为。
单元测试与其他测试
单元测试针对一个函数或方法,提供不同输入并检查结果是否符合预期。除了有效输入,也应考虑无效、缺失或边界数据,避免异常输入造成致命错误。
- 单元测试:隔离检查一小段代码,不依赖外部元素。
- 集成测试:检查不同部分能否协作,例如插件与 WordPress 数据库。本文使用真实 WordPress 测试环境。
- 功能测试:从用户角度检查整个系统的行为。
测试可以提前发现回归,提升维护信心,也帮助协作者了解改动影响。测试用例还会记录函数在特定数据下应有的行为,因而能作为代码行为的参考文档。
准备本地测试环境
WordPress 集成测试需要干净的 WordPress 环境以及相应依赖。常见方法有 NPM 与 Docker 的 wp-env,或 Composer。本文选择 Composer,作为教程中的简明做法。
请先准备 PHP、MySQL、SVN 和 WordPress CLI(WP-CLI)。MySQL 中需要有供测试使用的数据库,并准备好安装脚本所需的数据库用户名、密码和主机。测试环境安装会用 SVN 下载文件;WP-CLI 用于生成插件测试文件。
原文环境说明中的数据库名写作 wordpres_tests,后续安装命令则使用 wordpress_test。以下采用后续命令中的拼法;请确保本机数据库与脚本传入的值一致。原文账户示例为 root,项目应按自己的本地配置修改。
生成测试脚手架
用 WP-CLI 创建初始测试文件,将 plugin-name 替换成插件名:
wp scaffold plugin-tests plugin-name --ci=github
命令会生成以下常见文件:
.github/workflows/phpunit.yml:GitHub Actions 工作流;bin/install-wp-tests.sh:准备测试环境的 Bash 脚本;phpunit.xml.dist:PHPUnit 配置;tests/bootstrap.php:加载 WordPress、配置和插件的引导文件;tests/test-sample.php:示例测试。
这些文件提供起点,之后还需按项目结构调整测试目录、引导常量和 CI 配置。
安装 Composer 依赖并配置命令
在仓库中安装 WordPress PHPUnit 测试环境和兼容层:
composer require --dev yoast/phpunit-polyfills:^1.0 wp-phpunit/wp-phpunit:^6.3
wp-phpunit/wp-phpunit 是社区维护的 WordPress 测试环境实现,可省去手动克隆整个 WordPress 核心的步骤。yoast/phpunit-polyfills 为不同 PHPUnit 和 PHP 版本提供兼容层。
在 .gitignore 中忽略测试结果缓存和 Composer 依赖目录:
.phpunit.result.cache
vendor/
在 composer.json 的 scripts 中添加测试和安装命令。以下保留原文的数据库、账户、地址和 WordPress 版本示例:
"scripts": {
"test": "phpunit",
"test-install": "bash bin/install-wp-tests.sh wordpress_test root 'root' 127.0.0.1 latest"
}
test-install 参数依次指定数据库名、用户名、密码、主机和 WordPress 版本;示例主机为 127.0.0.1。按本机数据库配置调整这些值。接着安装测试环境:
composer test-install
原文建议检查 phpunit.xml.dist,从 <directory> 行移除 prefix="test-",以避免某些版本下的冲突;再把测试集中到 tests/Unit/。配置示例:
<testsuites>
<testsuite name="testing">
<directory suffix=".php">./tests/Unit/</directory>
</testsuite>
</testsuites>
把原来的示例测试文件移动到 tests/Unit/。目录名称可以按项目调整,但需与 PHPUnit 配置一致。
在 tests/bootstrap.php 开头定义插件和测试数据目录,并在环境未提供 WP_CORE_DIR 时从环境变量或临时目录确定路径:
define( 'TESTS_PLUGIN_DIR', dirname( __DIR__ ) );
define( 'UNIT_TESTS_DATA_PLUGIN_DIR', TESTS_PLUGIN_DIR . '/tests/Data/' ); // 可按项目调整。
// 如果 WP_CORE_DIR 尚未定义,则使用环境变量或临时目录。
if ( ! defined( 'WP_CORE_DIR' ) ) {
$_wp_core_dir = getenv( 'WP_CORE_DIR' );
if ( ! $_wp_core_dir ) {
$_wp_core_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress';
}
define( 'WP_CORE_DIR', $_wp_core_dir );
}
运行第一个测试
准备完成后,在项目根目录运行:
composer test
原文记录的示例结果是 PHPUnit 9.6.24 执行 1 项测试、1 条断言并通过。这是原文当时的输出示例,不代表本文在当前环境运行过测试。
如果出现 Could not find /wordpress-tests-lib/includes/functions.php,通常说明临时 WordPress 安装不完整。原文建议删除安装脚本报告的临时目录中的 wordpress-tests-lib 与 wordpress,再重新安装。以下保留原文示例路径;执行前确认该目录确实是本项目的临时测试环境:
rm -rfv /var/folders/kk/6287m8gj09xdkt2zgz432zhr0000gn/T/wordpress-tests-lib/
rm -rfv /var/folders/kk/6287m8gj09xdkt2zgz432zhr0000gn/T/wordpress/
composer test-install
使用 PHPUnit 断言
PHPUnit 提供多种断言,用于比较实际值与预期值。常用方法有:
assertTrue($condition)、assertFalse($condition):检查条件真假;assertEquals($expected, $actual):宽松比较;assertSame($expected, $actual):严格比较类型和值;assertNull()、assertNotNull():检查是否为null;assertEmpty()、assertNotEmpty():检查是否为空;assertCount()、assertContains():检查数量或成员;assertInstanceOf()、assertIsArray()、assertIsString()、assertIsInt():检查对象或类型;assertGreaterThan()、assertLessThan():比较数值。
原文用求和函数演示如何验证正常输入:
function plunit_sum( $a, $b ) {
return $a + $b;
}
public function test_sum_without_errors() {
$sum = plunit_sum( 4, 2 );
$this->assertEquals( 6, $sum );
}
也要为错误或缺失输入编写测试。原文示例把字符串 'hello' 和数字 2 传入求和函数,并期望结果为 0。要让断言成立,函数需要实现相应的输入校验或安全处理;单纯的直接加法实现并未展示这种逻辑。运行测试仍使用:
composer test
测试自定义文章类型
假设插件注册了名为 book 的自定义文章类型(CPT),可检查它是否注册、是否公开、是否出现在管理界面,以及能否创建对应文章。原文示例使用 WP_UnitTestCase 和 WordPress 测试工厂:
<?php
/**
* Integration tests for the Book custom post type.
*/
class Test_Book_CPT extends WP_UnitTestCase {
public function setUp(): void {
parent::setUp();
// Make sure our CPT is registered before each test.
mtp_register_cpt_book();
flush_rewrite_rules();
}
public function test_book_post_type_is_registered() {
$this->assertTrue( post_type_exists( 'book' ), 'The "book" post type should be registered.' );
}
public function test_book_post_type_is_public() {
$post_type_obj = get_post_type_object( 'book' );
$this->assertNotNull( $post_type_obj );
$this->assertTrue( $post_type_obj->public );
$this->assertTrue( $post_type_obj->show_ui );
}
public function test_can_create_book_post() {
$post_id = self::factory()->post->create(
array(
'post_type' => 'book',
'post_title' => 'Test Book',
)
);
$this->assertIsInt( $post_id );
$this->assertSame( 'book', get_post_type( $post_id ) );
$this->assertSame( 'Test Book', get_the_title( $post_id ) );
}
}
继承 WP_UnitTestCase 后,可以使用 PHPUnit 断言和 WordPress 测试工具,例如创建文章、用户和分类的工厂。基类也会让测试运行于 WordPress 测试环境。
setUp() 在每项测试前执行:先调用 parent::setUp(),再确保插件注册 CPT,并刷新重写规则,使测试从一致状态开始。若测试修改了其他全局状态,可视需要用 tearDown() 清理;测试工厂创建的数据通常由测试框架负责清理。
三项测试分别检查:post_type_exists( 'book' ) 为真;文章类型对象存在且 public、show_ui 为真;测试工厂创建的文章 ID 是整数、类型为 book,标题为 Test Book。这些断言能发现 slug 拼写错误、可见性标志错误或注册过程未运行等问题。
安装并加载插件依赖
若被测插件依赖其他插件,需要把依赖安装到测试环境,并从测试引导文件加载。原文以 WooCommerce 为例:在 bin/install-wp-tests.sh 的 install_wp 之前定义安装函数,并在 WordPress 安装完成后调用它:
# Installs WooCommerce plugin in the test environment
install_woocommerce() {
local PLUGIN_DIR="$WP_CORE_DIR/wp-content/plugins"
mkdir -p "$PLUGIN_DIR"
WOOCOMMERCE_URL="https://downloads.wordpress.org/plugin/woocommerce.zip"
download "$WOOCOMMERCE_URL" "$TMPDIR/woocommerce.zip"
unzip -q "$TMPDIR/woocommerce.zip" -d "$TMPDIR/"
rm -rf "$PLUGIN_DIR/woocommerce"
mv "$TMPDIR/woocommerce" "$PLUGIN_DIR/woocommerce"
echo "WooCommerce plugin installed successfully."
}
之后,在 tests/bootstrap.php 的 _manually_load_plugin 函数中加载 WooCommerce:
// Load WooCommerce first from the standard WordPress plugins directory.
require_once WP_CORE_DIR . '/wp-content/plugins/woocommerce/woocommerce.php';
其他依赖插件也应在安装脚本中准备好,并在引导代码中加载。
配置 GitHub Actions
原文建议检查 GitHub Actions 工作流里的主分支配置,让拉取请求触发测试:
on:
pull_request:
branches:
- trunk
将 trunk 替换为仓库实际使用的主分支。脚手架文件清单中工作流名为 phpunit.yml,本节示例使用 testing.yml;应编辑仓库中实际存在的工作流文件。
原文还建议检查 PHP 配置,并显式安装 SVN,因为 GitHub Actions 环境默认不包含 SVN:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: phpunit-polyfills:1.1
extensions: mbstring, xml, zip, intl, pdo, mysql
coverage: none
- name: Install SVN
run: sudo apt-get update && sudo apt-get install -y subversion
PHP 版本、扩展和工具应与项目依赖及测试环境一致。配置完成后,向指定主分支发起拉取请求时,CI 会自动运行测试。
小结
从 WP-CLI 脚手架开始,使用 Composer 安装测试依赖,再配置数据库、PHPUnit 和引导文件,就能在本机运行 WordPress 插件测试。接下来为关键函数和功能补充断言,并把测试集成到 GitHub Actions,让每次拉取请求自动触发检查。
示例从自定义文章类型入手,也可以继续覆盖插件中的函数、类和钩子。David Perez 在原文末尾感谢 @milana_cap、@bph、@greenshady、@juanmaguitar 和 @areziaal 提供审阅反馈。
来源与作者信息:David Perez,《How to add automated unit tests to your WordPress plugin》,WordPress Developer Blog,2025-12-16。 原文链接:https://developer.wordpress.org/news/2025/12/how-to-add-automated-unit-tests-to-your-wordpress-plugin/











暂无评论内容