- CMS
- 后端
- 前端
【免费下载链接】october
Self-hosted CMS platform based on the Laravel PHP Framework.
本篇技术指南以 October CMS(基于 Laravel 的自托管 CMS 平台)官方测试文档为主体,系统讲解如何为插件编写并运行单元测试:从插件根目录的phpunit.xml配置、tests/目录组织与PluginTestCase基类,到内存数据库的初始化原理、多插件协同测试以及数据库引擎切换,最后覆盖核心框架的系统级测试。读完本文,你将能独立为任意 October CMS 插件搭建可重复、隔离的测试环境,并理解其底层实现机制。
测试体系总览:插件测试与系统测试
October CMS 将自动化测试划分为两个层次,两者对应不同的运行入口与测试范围:
| 层次 | 运行方式 | 测试对象 | 前置条件 |
|---|---|---|---|
| 插件测试(Plugin testing) | 在插件根目录执行phpunit | 某个具体插件(/plugins/acme/blog等) | 插件目录内包含phpunit.xml与tests/目录 |
| 系统测试(System testing) | 在仓库根目录执行phpunit | October 核心模块(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/目录存放测试类。官方对组织方式有两条明确约定:
- 目录结构模仿插件源码结构:即
tests/Models/、tests/Classes/等,与models/、classes/一一对应; - 类名加
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(),清理跨测试残留的模型事件监听器。
基类还暴露了四个可覆写的行为开关属性:
| 属性 | 默认值 | 作用 |
|---|---|---|
$autoMigrate | true | 是否在setUp时执行核心与当前插件的数据库迁移 |
$useTransactions | false | 为true时整个进程只迁移一次数据库,每个测试在事务内执行并回滚(大幅提速) |
$autoMigrateTailor | false | 是否迁移 Tailor 蓝图(blueprint),与useTransactions组合时每个进程仅执行一次 |
$autoRegister | true | 是否在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:的设置一致)。如果需要覆盖默认行为,官方提供两种方式:
- 启用配置驱动:在
/config/database.php中将useConfigForTesting配置项设为true。当APP_ENV为testing且useConfigForTesting为true时,数据库参数将从/config/database.php读取。需要说明的是,当前仓库的 config/database.php 并未预置useConfigForTesting键,使用前需自行添加该配置项。 - 覆盖测试专属配置:通过创建
/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.
相关推荐
October CMS单元测试覆盖率提升:从基础测试到集成测试
October CMS单元测试覆盖率提升:从基础测试到集成测试 引言:为什么测试覆盖率对October CMS至关重要 你是否曾在October CMS插件更新
CMS后端前端掌握BootstrapBlazor提示组件:位置与偏移的终极指南 🎯
掌握BootstrapBlazor提示组件:位置与偏移的终极指南 🎯 BootstrapBlazor提示组件(Tooltip)是Blazor开发中不可或缺的U
UI组件前端Vuetify 单元测试完全指南:从 Vite/Vitest 配置到浏览器级组件测试
Vuetify 单元测试完全指南:从 Vite/Vitest 配置到浏览器级组件测试 单元测试是保障 Vuetify 应用质量的关键防线,它能够在组件重构、依赖
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考