本文目录导读:

- 场景区分(先明确你在测什么)
- 如果你在“开发扩展包”本身(推荐方案)
- 如果你在“宿主项目”中测试扩展包(集成测试)
- 常用测试 PHP 扩展包的独立技巧
- 几个特别重要的配置细节
- 一键完事:使用官方模板(强烈推荐)
- 快速排查问题(遇到测试失败时)
- 总结:什么时候该用什么方案?
在 PHP 项目中测试扩展包(Package/Composer 包),有几种不同的场景和方法,下面按照从“开发包本身”到“集成到项目”的逻辑,整理出完整的测试方案:
场景区分(先明确你在测什么)
- 开发扩展包本身(你有包的源码)
- 在宿主项目中使用第三方扩展包(验证是否与项目兼容)
- 本地修改扩展包,想在项目里直接生效(本地开发调试)
不同的场景,测试方法完全不同。
如果你在“开发扩展包”本身(推荐方案)
这是最正规的测试方式,在包的根目录下建立测试环境:
安装测试依赖
在扩展包的 composer.json 中加入 require-dev(测试必备):
{
"require-dev": {
"phpunit/phpunit": "^9.6 || ^10.5",
"mockery/mockery": "^1.5",
"orchestra/testbench": "^8.0",
"phpstan/phpstan": "^1.0"
}
}
注意:
orchestra/testbench是专门为 Laravel 包开发准备的,它提供了一个迷你的 Laravel 测试基础环境,避免运行完整的 Laravel 应用。
创建测试骨架
my-package/
├── src/ # 源码
├── tests/ # 测试代码
│ ├── Unit/ # 单元测试
│ ├── Feature/ # 集成测试(Laravel 包时)
│ └── TestCase.php # 继承 Testbench 的基类
├── phpunit.xml.dist # PHPUnit 配置文件
└── composer.json
TestCase.php 的标准写法(Laravel 包):
<?php
namespace Vendor\MyPackage\Tests;
use Orchestra\Testbench\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
protected function getPackageProviders($app)
{
return [
\Vendor\MyPackage\MyPackageServiceProvider::class,
];
}
protected function getEnvironmentSetUp($app)
{
// 设置测试用的数据库配置、环境变量等
$app['config']->set('database.default', 'testing');
}
}
运行测试命令
# 安装开发依赖(注意 --dev 默认会装) composer install # 运行全部测试 vendor/bin/phpunit # 运行指定测试类(按行号精确调试) vendor/bin/phpunit --filter testMyFunction tests/Feature/MyTest.php # 生成代码覆盖率(需要 xdebug) vendor/bin/phpunit --coverage-html ./coverage
测试数据库(如果有数据库操作)
方案A:使用 SQLite 内存数据库(最快)
// 在 testbench 中
$app['config']->set('database.default', 'sqlite');
$app['config']->set('database.connections.sqlite.database', ':memory:');
方案B:使用 MySQL 测试库(更接近生产)
在 phpunit.xml.dist 里配置:
<php>
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_DATABASE" value="test_db"/>
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value="secret"/>
</php>
如果你在“宿主项目”中测试扩展包(集成测试)
这里分为两种情况:
情况 1:使用 Composer 安装的正式包
# 安装包 composer require vendor/package # 运行项目本身的测试(如果包有行为,应该在项目中写功能测试) vendor/bin/phpunit tests/Feature/FooTest.php
情况 2:本地修改的包,希望项目直接使用(开发调试)
这是最常用的本地联调方式,修改 composer.json 的 repositories:
{
"repositories": [
{
"type": "path",
"url": "../local/packages/my-package",
"options": {
"symlink": true
}
}
],
"require": {
"vendor/my-package": "@dev"
}
}
composer update vendor/my-package
设置 symlink: true 后,你的项目会软链接到本地包源码,这样你在本地包中改代码,项目测试立即生效,不用每次重新 composer update。
常用测试 PHP 扩展包的独立技巧
使用 Laravel Dusk 测试前端交互(如果包含 JS/UI)
// 在宿主项目中
public function test_package_renders_ui()
{
$this->browse(function ($browser) use ($user) {
$browser->visit('/package/route')
->assertSee('Package UI Loaded')
->click('#submit')
->assertSee('Success');
});
}
测试 Facade / 容器绑定(Laravel 风格)
public function test_facade_works()
{
$this->app->instance('my-package', new MyService());
$result = MyPackage::doSomething();
$this->assertEquals('expected', $result);
}
模拟 HTTP 请求(如果包会调用外部 API)
use Http;
public function test_external_api_call()
{
Http::fake([
'api.example.com/*' => Http::response(['status' => 'ok'], 200),
]);
$this->app['config']->set('my-package.api_key', 'test-key');
$result = MyService::callAPI();
$this->assertEquals('ok', $result['status']);
}
测试数据库迁移(包自带 migrations)
public function test_migrations_run_successfully()
{
// 加载包内迁移
$this->artisan('migrate', ['--path' => base_path('vendor/package/database/migrations')])->assertExitCode(0);
}
几个特别重要的配置细节
| 问题 | 解决方式 |
|---|---|
| 包有路由/配置文件 | 测试前执行 php artisan vendor:publish --provider="Vendor\Package\ServiceProvider" |
| 包需要特定 PHP 版本 | 在 composer.json 的 require 中指定 "php": "^8.1",但在测试机器上确保 PHP 版本一致 |
| 测试时不想碰真实数据库 | 每个测试类 use RefreshDatabase; 在最后自动回滚事务 |
| 包有 Redis/Cache 依赖 | 在 tests/TestCase.php 里设置 Cache::store('array')->... 防误操作 |
| 测试包时自动发现测试文件 | 确保 composer.json 中有:"autoload-dev": { "psr-4": { "Vendor\\Package\\Tests\\": "tests/" } } |
一键完事:使用官方模板(强烈推荐)
如果你是自己写包,直接用官方脚手架(避免踩坑):
composer create-project laravel/laravel my-package --prefer-dist composer require orchestra/testbench --dev
或者用社区维护的包模板:
composer create-project --prefer-dist php-package-template/php-package-template my-package
快速排查问题(遇到测试失败时)
- 先看是否是包自动加载问题:运行
composer dump-autoload - 检查容器绑定:
php artisan tinker里执行app(\Vendor\Package\Class::class) - 扩大日志:在测试环境临时设置
LOG_CHANNEL=stderr或LOG_LEVEL=debug - 用 PHPUnit
--debug:查看每个步骤是否执行到预期位置 - 数据库异常:确保测试库的迁移文件路径正确,必要时在
phpunit.xml中显式指定DB_DATABASE为测试专用库
什么时候该用什么方案?
| 场景 | 推荐方案 |
|---|---|
| 写新包 | orchestra/testbench + PHPUnit(包内独立测) |
| 包已发布,在项目中使用 | 项目内写功能测试,正常引包测试 |
| 本地改包 + 项目调试 | path repository + symlink,项目内跟随包源码更新测试 |
| 只做静态检测 | PHPStan + psalm(可能的话加 Rector) |
| CI 自动化 | GitHub Actions / GitLab CI 写全套流程 |
特别提醒:不要直接在生产环境的 composer.json 里修改包的源码做测试,那会污染生产依赖,永远在 require-dev 下做测试。