ousaa / tp51-phpunit-setup
一键为 ThinkPHP 5.1 项目配置 PHPUnit 测试环境
Package info
gitlab.seastt.com/ousaa/php-packages/tp51-phpunit-setup.git
pkg:composer/ousaa/tp51-phpunit-setup
Requires
- php: >=7.4
Requires (Dev)
- phpunit/phpunit: ^9.6
README
一键为 ThinkPHP 5.1 项目配置 PHPUnit 测试环境。
将原本需要手动完成的五个步骤压缩为两条命令:
composer require --dev ousaa/tp51-phpunit-setup
./vendor/bin/tp51phpunit-setup
执行完毕后即可直接运行:
./vendor/bin/phpunit
要求
- PHP
^7.4 - ThinkPHP
5.1.x - Composer
安装与使用
第一步:安装包
composer require --dev ousaa/tp51-phpunit-setup
第二步:运行 setup 脚本
在项目根目录(composer.json 所在目录)执行:
./vendor/bin/tp51phpunit-setup
脚本会自动完成以下操作:
- 修改
composer.json:添加think\命名空间的 PSR-4 映射、tests\命名空间的 autoload-dev、以及phpunit/phpunit ^9.5的 require-dev 依赖 - 给
Loader.php打补丁:将thinkphp/library/think/Loader.php中的require autoload_static.php改为include_once,避免 phpunit 启动时报重复声明类的错误 - 复制测试基础文件:逐文件对比
tests/模板,只复制不存在的文件,已有文件保持不变 - 复制
phpunit.xml到项目根目录(已存在则跳过) - 执行
composer dump-autoload更新自动加载
第三步:安装 phpunit
composer update phpunit/phpunit
第四步:运行测试
./vendor/bin/phpunit
强制更新(覆盖模板文件)
当包升级后,若需要将最新的 bootstrap.php、HttpTestCase.php 等模板文件同步到项目,使用:
./vendor/bin/tp51phpunit-force-setup
与标准安装的区别:逐文件强制覆盖模板中的所有文件(只替换模板有的文件,不会删除项目自有的其他测试文件)。
幂等性
标准安装脚本可以安全地重复执行,已存在的配置和文件会被跳过,不会覆盖或破坏已有内容。
生成的目录结构
项目根目录/
├── phpunit.xml ← 新增
├── tests/
│ ├── README.md ← 新增(测试基础设施说明文档)
│ ├── bootstrap.php ← 新增
│ ├── TestRequest.php ← 新增
│ ├── TestApp.php ← 新增
│ ├── HttpTestCase.php ← 新增
│ ├── Feature/
│ │ ├── Controllers/ ← 推荐按类型建子目录
│ │ └── ExampleHttpTest.php ← 示例,需按项目路由调整后验证
│ └── Unit/
│ ├── Dao/ ← 推荐:Model / Db 测试放这里
│ └── ExampleTest.php ← 示例,可删除
命名规范
目录结构
按被测对象类型分子目录:
tests/
├── Feature/
│ ├── Controllers/ # 对应 app/http/controller/
│ ├── Services/ # 对应 app/common/service/(如需要)
│ └── Utils/
└── Unit/
├── Controllers/
├── Services/
├── Dao/ # 对应 Model 类 / Db 直接查询
└── Utils/
文件命名
测试文件名与被测类名对应,加 Test 后缀:
| 被测文件 | 测试文件 |
|---|---|
app/http/controller/UserController.php | tests/Feature/Controllers/UserControllerTest.php |
app/common/service/OrderService.php | tests/Unit/Services/OrderServiceTest.php |
app/common/model/User.php | tests/Unit/Dao/UserModelTest.php |
方法命名
采用三段式命名:test{TargetMethod}_{Scenario}_{ExpectedResult}
testGetUser_WithValidId_ShouldReturnUserObject
testLogin_WithWrongPassword_ShouldReturn401
testCreate_WithValidData_ShouldReturnModelInstance
testDelete_WithExistingRecord_ShouldRemoveFromDb
编写测试
Unit 测试(含 Db / Model)
bootstrap.php 在所有测试运行前全局初始化 ThinkPHP App,因此直接继承 PHPUnit\Framework\TestCase 即可使用 Db 类和 Model 类,无需任何额外的基类:
<?php
namespace tests\Unit\Dao;
use think\Db;
use app\common\model\User;
class UserModelTest extends \PHPUnit\Framework\TestCase
{
const TEST_PREFIX = '__test__';
protected function tearDown(): void
{
// 测试结束后清除本次产生的数据,保证幂等
User::where('account', 'like', self::TEST_PREFIX . '%')->delete();
}
public function testCreate_WithValidData_ShouldReturnModelInstance(): void
{
$user = User::create([
'account' => self::TEST_PREFIX . 'alice',
'nickname' => 'Alice',
'password' => md5('secret'),
]);
$this->assertInstanceOf(User::class, $user);
$this->assertGreaterThan(0, $user->id);
}
public function testFind_WithExistingId_ShouldReturnCorrectRecord(): void
{
$created = User::create([
'account' => self::TEST_PREFIX . 'bob',
'nickname' => 'Bob',
'password' => md5('secret'),
]);
$found = User::find($created->id);
$this->assertNotNull($found);
$this->assertEquals('Bob', $found->nickname);
}
}
Feature 测试(模拟 HTTP 请求)
继承 tests\HttpTestCase,使用 request() 或 requestJson() 方法:
<?php
namespace tests\Feature\Controllers;
use tests\HttpTestCase;
class UserControllerTest extends HttpTestCase
{
public function testGetUser_WithValidId_ShouldReturnUserObject(): void
{
$response = $this->requestJson('GET', '/api/user/1');
$this->assertEquals(200, $response->getCode());
$data = json_decode($response->getContent(), true);
$this->assertArrayHasKey('id', $data['data'] ?? []);
}
public function testUpdate_WithValidData_ShouldReturn200(): void
{
$response = $this->requestJson('PUT', '/api/user/1', ['nickname' => 'updated']);
$this->assertEquals(200, $response->getCode());
}
}
| 方法 | Content-Type | body 编码 |
|---|---|---|
request('POST', ...) | 不设置 | 表单($_POST) |
requestJson('POST', ...) | application/json | JSON(php://input) |
requestJson('PUT', ...) | application/json | JSON(php://input) |
requestJson('GET', ...) | 不设置 | 无 |
注意事项
1. 运行目录
两个脚本都必须在项目根目录执行,通过 getcwd() 定位宿主项目。
2. Loader.php 版本
setup 脚本对 thinkphp/library/think/Loader.php 使用多行精确匹配进行补丁,若 ThinkPHP 版本不匹配导致找不到预期内容,脚本会打印错误并中止,请手动将对应行的 require 改为 include_once。
3. 数据库配置
测试直接读取项目根目录的 .env 文件中的数据库配置,无需额外的 .env.test。
4. 不在本包范围内的事项
requestByUser/requestJsonByUser等项目特定认证方法需自行在HttpTestCase子类中添加- 不提供 Composer 插件,用户需显式运行 setup 命令
许可证
MIT