☰
October CMS 单元测试完全指南:从插件 phpunit.xml 配置到 PluginTestCase 与系统测试
2026/10/8 1:52:32 网站建设 项目流程
  • CMS
  • 后端
  • 前端

【免费下载链接】october

Self-hosted CMS platform based on the Laravel PHP Framework.

项目地址:https://gitcode.com/gh_mirrors/oc/october
点击查看免费下载

本篇技术指南以 October CMS(基于 Laravel 的自托管 CMS 平台)官方测试文档为主体,系统讲解如何为插件编写并运行单元测试:从插件根目录的phpunit.xml配置、tests/目录组织与PluginTestCase基类,到内存数据库的初始化原理、多插件协同测试以及数据库引擎切换,最后覆盖核心框架的系统级测试。读完本文,你将能独立为任意 October CMS 插件搭建可重复、隔离的测试环境,并理解其底层实现机制。

测试体系总览:插件测试与系统测试

October CMS 将自动化测试划分为两个层次,两者对应不同的运行入口与测试范围:

层次运行方式测试对象前置条件
插件测试(Plugin testing)在插件根目录执行phpunit某个具体插件(/plugins/acme/blog等)插件目录内包含phpunit.xml与tests/目录
系统测试(System testing)在仓库根目录执行phpunitOctober 核心模块(backend、cms、system 等)通过 Composer 或 git clone 获取的开发副本(含tests/目录)

二者的核心差异在于测试套件(testsuite)的指向与引导脚本(bootstrap)的不同,下文分别展开。

插件测试:快速开始

官方文档给出的最快路径是:在插件基础目录(Base Plugin Directory,即/plugins/<作者>/<插件名>)直接运行phpunit。前提是该目录下已存在 PHPUnit 配置文件。

创建插件的 phpunit.xml

在插件根目录(以/plugins/acme/blog/phpunit.xml为例)创建如下phpunit.xml,这是官方推荐的完整模板:

<?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" bootstrap="../../../tests/bootstrap.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" > <testsuites> <testsuite name="Plugin Unit Test Suite"> <directory>./tests</directory> </testsuite> </testsuites> <php> <env name="APP_ENV" value="testing"/> <env name="CACHE_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/> </php> </phpunit>

各配置项的实战含义如下:

  • bootstrap="../../../tests/bootstrap.php":这是插件测试与 October 测试基建衔接的关键。从/plugins/acme/blog向上三级即仓库根目录,根目录下的 tests/bootstrap.php 只做了一件事——引入核心测试引导文件:

    require __DIR__ . '/../modules/system/tests/bootstrap.php';

    而 modules/system/tests/bootstrap.php 依次加载了TestCase.php、PluginTestCase.php,引入 October 自动加载器(bootstrap/autoload.php),并注册一个回退自动加载器,将App\命名空间映射到app/,同时把modules、plugins目录纳入类加载路径。也就是说,插件的phpunit.xml通过这一串引导链拿到了完整的 October 运行时。

  • backupGlobals="false"/backupStaticAttributes="false":不对全局变量与静态属性做备份,以降低测试开销——这也是PluginTestCase之所以需要在tearDown中手动清理模型事件监听器的原因之一(见下文)。

  • convertErrorsToExceptions="true"/convertNoticesToExceptions="true"/convertWarningsToExceptions="true":将 PHP 的 error、notice、warning 统一提升为异常,保证测试失败信息可被捕获。

  • processIsolation="false"/stopOnFailure="false":不在独立进程运行、失败后继续执行,加快测试速度并便于收集完整失败清单。

  • <directory>./tests</directory>:指定测试套件扫描tests/目录下所有*Test.php类。

  • <env>区块:将APP_ENV设为testing,并把缓存与会话驱动切换为array(内存态),避免测试污染文件系统缓存。这些环境变量会被写入$_ENV/$_SERVER,进而被config/下的env()读取。

组织 tests 目录与测试类

配置完成后,在插件根目录创建tests/目录存放测试类。官方对组织方式有两条明确约定:

  1. 目录结构模仿插件源码结构:即tests/Models/、tests/Classes/等,与models/、classes/一一对应;
  2. 类名加Test后缀,并强烈建议使用命名空间。

官方示例(假设插件为Acme.Blog):

<?php namespace Acme\Blog\Tests\Models; use Acme\Blog\Models\Post; use PluginTestCase; class PostTest extends PluginTestCase { public function testCreateFirstPost() { $post = Post::create(['title' => 'Hi!']); $this->assertEquals(1, $post->id); } }

注意这里Post::create(['title' => 'Hi!'])断言$post->id === 1——因为每个测试运行在全新迁移的内存数据库中,这是第一条记录,主键从 1 开始。这段代码直观体现了插件测试的"隔离"特性。

PluginTestCase 基类:内存数据库与插件刷新的实现原理

官方文档指出,测试类必须继承PluginTestCase。这是一个特殊基类,会在setUp阶段建立存储在内存中的 October 数据库,并刷新当前被测试插件及其所有声明的依赖插件。其行为等价于在每次测试前依次执行:

php artisan october:up php artisan plugin:refresh Acme.Blog [php artisan plugin:refresh <dependency>, ...]

(对应命令实现见 modules/system/console/OctoberUp.php 与 modules/system/console/PluginRefresh.php。)

结合 modules/system/tests/PluginTestCase.php 源码,可以还原这一流程的完整实现:

  • createApplication()通过bootstrap/app.php创建 Laravel 应用,并单例注册后端认证管理器(AuthManager);
  • setUp()按顺序执行:重置插件迁移缓存 → 创建应用实例 → 若autoRegister为true则加载当前插件 → (可选)复用先前迁移过的内存数据库 → 若autoMigrate为true则迁移核心模块与当前插件 → 若useTransactions为true则开启事务隔离 → 最后调用Mail::pretend()屏蔽真实邮件发送;
  • tearDown()回滚事务(若开启)、并通过反射遍历所有已声明的 October 模型类执行flushEventListeners(),清理跨测试残留的模型事件监听器。

基类还暴露了四个可覆写的行为开关属性:

属性默认值作用
$autoMigratetrue是否在setUp时执行核心与当前插件的数据库迁移
$useTransactionsfalse为true时整个进程只迁移一次数据库,每个测试在事务内执行并回滚(大幅提速)
$autoMigrateTailorfalse是否迁移 Tailor 蓝图(blueprint),与useTransactions组合时每个进程仅执行一次
$autoRegistertrue是否在setUp时注册并启动当前插件

"刷新当前插件及其依赖"的具体逻辑位于 modules/system/tests/concerns/PerformsMigrations.php:migrateCurrentPlugin()通过guessPluginCodeFromTest()从测试文件的物理路径反推出插件编码(如Acme.Blog),migratePluginInternal()会先加载插件,再遍历其require属性中声明的依赖插件递归迁移,最后对每个插件执行回滚 + 迁移,等效于plugin:refresh。该 trait 还提供了migrateDatabase()、migratePlugin($code)等辅助方法(旧 APIrunPluginRefreshCommand()、runOctoberMigrateCommand()已标记为@deprecated)。

测试与其他插件协同:BaseTestCase 模式与配置文件注册

官方文档特别提醒了一个易踩的坑:如果插件使用配置文件(file configuration),就必须在setUp中调用System\Classes\PluginManager::instance()->registerAll(true);,否则配置文件相关的特性不会生效。

官方给出的推荐做法是定义自己的基类测试用例,用于"与其他插件协同测试"而非"插件孤立测试":

use System\Classes\PluginManager; class BaseTestCase extends PluginTestCase { public function setUp(): void { parent::setUp(); // Get the plugin manager $pluginManager = PluginManager::instance(); // Register the plugins to make features like file configuration available $pluginManager->registerAll(true); // Boot all the plugins to test with dependencies of this plugin $pluginManager->bootAll(true); } public function tearDown(): void { parent::tearDown(); // Get the plugin manager $pluginManager = PluginManager::instance(); // Ensure that plugins are registered again for the next test $pluginManager->unregisterAll(); } }

registerAll(true)与bootAll(true)会注册并启动环境中所有插件(PluginManager),从而使当前插件的依赖及其配置文件在测试中可用;tearDown中的unregisterAll()则确保下一个测试重新注册,避免跨测试状态污染。

更换插件测试的数据库引擎

默认情况下,插件测试环境使用存储在内存中的 SQLite(即:memory:,与根目录 phpunit.xml 中DB_CONNECTION=sqlite、DB_DATABASE=:memory:的设置一致)。如果需要覆盖默认行为,官方提供两种方式:

  1. 启用配置驱动:在/config/database.php中将useConfigForTesting配置项设为true。当APP_ENV为testing且useConfigForTesting为true时,数据库参数将从/config/database.php读取。需要说明的是,当前仓库的 config/database.php 并未预置useConfigForTesting键,使用前需自行添加该配置项。
  2. 覆盖测试专属配置:通过创建/config/testing/database.php覆盖/config/database.php。当存在该文件时,测试环境将优先使用其中的变量(后者覆盖前者)。

这两种方式为需要真实数据库(如 MySQL)验证 SQL 兼容性或特定方言特性的插件测试提供了灵活的出口。

系统测试:核心框架的单元测试

要测试 October 核心文件(backend、cms、system、tailor 等模块),需要先通过Composer 安装或 git clone 获取开发副本,以确保仓库中包含tests/目录。

系统测试的运行方式同样简单:在仓库根目录(或在/tests/unit目录内)执行phpunit。根目录的 phpunit.xml 是系统测试的正式配置,其要点与插件配置形成对照:

  • bootstrap="modules/system/tests/bootstrap.php":直接引导核心测试基建(而非经由tests/bootstrap.php转发);
  • testsuite 指向./modules/*/tests:一个测试套件覆盖全部核心模块的测试目录;
  • 环境变量更为完整:APP_ENV=testing、CACHE_DRIVER=array、SESSION_DRIVER=array、ACTIVE_THEME=test、PLUGINS_PATH指向modules/system/tests/fixtures/plugins、THEMES_PATH指向modules/cms/tests/fixtures/themes、ENABLE_CSRF=false、DB_CONNECTION=sqlite、DB_DATABASE=:memory:。

从当前仓库结构看,tests/目录下仅有README.md与bootstrap.php,实际的系统测试引导与测试用例分散在各模块的tests/子目录中(如 modules/system/tests/ 包含PluginTestCase.php、TestCase.php、concerns/、fixtures/等),这也解释了文档"在根目录运行phpunit"与"/tests/unit内运行"两种写法的等价来源——它们共享同一套引导链与内存数据库约定。

仓库内可直接研读的测试资源

想要深入理解上述机制的读者,建议按以下路径在仓库中对照阅读:

  • tests/bootstrap.php 与 modules/system/tests/bootstrap.php:插件测试与系统测试共同的引导入口;
  • modules/system/tests/PluginTestCase.php:PluginTestCase基类的完整实现与四个行为开关;
  • modules/system/tests/concerns/PerformsMigrations.php:内存数据库迁移、插件依赖递归刷新、事务隔离的具体实现;
  • phpunit.xml:根目录系统测试配置(modules/*/tests套件与全部测试环境变量);
  • modules/system/console/OctoberUp.php 与 modules/system/console/PluginRefresh.php:october:up与plugin:refresh命令实现;
  • config/database.php:数据库连接配置(sqlite/mysql/mariadb/pgsql/sqlsrv),是自定义测试数据库的基础。

综上,October CMS 的测试体系围绕"内存 SQLite + 每次测试前迁移刷新"这一核心约定展开:插件测试通过插件的phpunit.xml接入根目录引导链,借助PluginTestCase获得与artisan october:up && plugin:refresh等价的数据库初始化能力;系统测试则在根目录phpunit.xml下覆盖全部核心模块。理解这层机制后,无论编写插件单测还是贡献核心代码,都能快速定位测试环境并写出稳定、隔离的测试用例。

  • CMS
  • 后端
  • 前端

【免费下载链接】october

Self-hosted CMS platform based on the Laravel PHP Framework.

项目地址:https://gitcode.com/gh_mirrors/oc/october
点击查看免费下载
上一篇:LinkSwift 教程:8 大网盘网页端快速提取网盘直链,交给 IDM / Aria2 下载
下一篇:京东抢购助手终极使用指南:从零开始掌握自动抢购技巧

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询