PHP 项目怎么测试扩展包

wen PHP项目 7

本文目录导读:

PHP 项目怎么测试扩展包

  1. 场景区分(先明确你在测什么)
  2. 如果你在“开发扩展包”本身(推荐方案)
  3. 如果你在“宿主项目”中测试扩展包(集成测试)
  4. 常用测试 PHP 扩展包的独立技巧
  5. 几个特别重要的配置细节
  6. 一键完事:使用官方模板(强烈推荐)
  7. 快速排查问题(遇到测试失败时)
  8. 总结:什么时候该用什么方案?

在 PHP 项目中测试扩展包(Package/Composer 包),有几种不同的场景和方法,下面按照从“开发包本身”到“集成到项目”的逻辑,整理出完整的测试方案:

场景区分(先明确你在测什么)

  1. 开发扩展包本身(你有包的源码)
  2. 在宿主项目中使用第三方扩展包(验证是否与项目兼容)
  3. 本地修改扩展包,想在项目里直接生效(本地开发调试)

不同的场景,测试方法完全不同。


如果你在“开发扩展包”本身(推荐方案)

这是最正规的测试方式,在包的根目录下建立测试环境:

安装测试依赖

在扩展包的 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.jsonrepositories

{
    "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.jsonrequire 中指定 "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

快速排查问题(遇到测试失败时)

  1. 先看是否是包自动加载问题:运行 composer dump-autoload
  2. 检查容器绑定php artisan tinker 里执行 app(\Vendor\Package\Class::class)
  3. 扩大日志:在测试环境临时设置 LOG_CHANNEL=stderrLOG_LEVEL=debug
  4. 用 PHPUnit --debug:查看每个步骤是否执行到预期位置
  5. 数据库异常:确保测试库的迁移文件路径正确,必要时在 phpunit.xml 中显式指定 DB_DATABASE 为测试专用库

什么时候该用什么方案?

场景 推荐方案
写新包 orchestra/testbench + PHPUnit(包内独立测)
包已发布,在项目中使用 项目内写功能测试,正常引包测试
本地改包 + 项目调试 path repository + symlink,项目内跟随包源码更新测试
只做静态检测 PHPStan + psalm(可能的话加 Rector)
CI 自动化 GitHub Actions / GitLab CI 写全套流程

特别提醒:不要直接在生产环境的 composer.json 里修改包的源码做测试,那会污染生产依赖,永远在 require-dev 下做测试。

抱歉,评论功能暂时关闭!