ThinkPHP单元测试需通过think-testing扩展搭建,配置phpunit.xml指定测试目录与环境,继承TestCase类模拟请求;控制器应解耦业务逻辑,用actingAs()模拟登录、RefreshDatabase保持数据库干净、Mock外部依赖;代码覆盖率需用PCOV生成HTML报告,重点关注边界场景而非单纯行覆盖率。

ThinkPHP 的单元测试不是“跑一遍看看有没有报错”,而是通过可控的模拟环境,验证控制器、模型、服务等核心逻辑是否按预期工作;代码覆盖率则是帮你看清哪些代码真正被测试“触达”过,避免盲目自信。
ThinkPHP 单元测试怎么搭起来
安装
think-testing扩展(推荐用于 TP6+):composer require topthink/think-testing --dev
它封装了 HTTP 请求模拟、数据库重置、登录态伪造等常用能力,比裸用 PHPUnit 更贴合框架习惯。配置
phpunit.xml,明确测试目录和环境变量:
确保<directory suffix="Test.php">./tests</directory>指向你的测试文件夹;
加上<env name="APP_ENV" value="testing"/>,让框架加载测试专用配置(如内存数据库、关闭日志等)。-
创建测试类,继承
TestCase,用$this->get()、$this->post()发起模拟请求:public function testIndexPageReturns200() { $response = $this->get('/'); $response->assertStatus(200); $response->assertSee('欢迎'); }不需要启动 Web 服务器,也不依赖真实路由,纯 PHP 层面完成验证。
立即学习“PHP免费学习笔记(深入)”;
ThinkPHP 控制器测试的关键点
避免在控制器里写大量业务逻辑(即所谓“胖控制器”):
把数据处理、权限校验、第三方调用等抽到 Service 或 Command 类中,控制器只负责协调和响应。这样测试时更容易 mock 依赖,也更聚焦接口契约。-
利用
actingAs()模拟用户登录状态:$user = User::factory()->create(); $this->actingAs($user)->get('/admin/dashboard')->assertOk(); 使用
RefreshDatabasetrait 保证每次测试前数据库干净:
在测试类顶部加use RefreshDatabase;,它会在每个测试前后自动迁移并清空测试库(需配置好TESTING_DB_*环境变量)。对外部依赖(如微信 SDK、短信网关)做 Mock:
用Mockery::mock()或 PHPUnit 自带的createMock()替换服务实例,控制返回值,避免测试受网络或第三方稳定性影响。
代码覆盖率怎么生成和看懂
-
安装 Xdebug 或 PCOV(推荐 PCOV,轻量且速度快):
pecl install pcov,然后在php.ini中启用:extension=pcov pcov.enabled=1
-
在
phpunit.xml中添加 coverage 配置段:<coverage processUncoveredFiles="true"> <include> <directory suffix=".php">app</directory> </include> <exclude> <directory>tests</directory> <file>app/Console/Kernel.php</file> </exclude> <report> <html outputDirectory="coverage"/> </report> </coverage> 运行命令生成报告:
./vendor/bin/phpunit --coverage-html coverage
打开coverage/index.html,绿色表示已覆盖,红色表示未执行,黄色是部分分支未走全(比如 if 有 else 但没测 else 分支)。注意:80% 行覆盖率 ≠ 高质量测试。重点看关键路径是否覆盖——比如登录失败、参数缺失、权限不足、数据库异常等边界情况,比反复调用成功流程更有价值。
不复杂但容易忽略



















