ousaa/tp51-phpunit-setup

一键为 ThinkPHP 5.1 项目配置 PHPUnit 测试环境

Maintainers

Package info

gitlab.seastt.com/ousaa/php-packages/tp51-phpunit-setup.git

pkg:composer/ousaa/tp51-phpunit-setup

Statistics

Installs: 17

Dependents: 0

Suggesters: 0

v1.1.1 2026-05-20 04:45 UTC

This package is auto-updated.

Last update: 2026-05-20 04:46:23 UTC


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

脚本会自动完成以下操作:

  1. 修改 composer.json:添加 think\ 命名空间的 PSR-4 映射、tests\ 命名空间的 autoload-dev、以及 phpunit/phpunit ^9.5 的 require-dev 依赖
  2. Loader.php 打补丁:将 thinkphp/library/think/Loader.php 中的 require autoload_static.php 改为 include_once,避免 phpunit 启动时报重复声明类的错误
  3. 复制测试基础文件:逐文件对比 tests/ 模板,只复制不存在的文件,已有文件保持不变
  4. 复制 phpunit.xml 到项目根目录(已存在则跳过)
  5. 执行 composer dump-autoload 更新自动加载

第三步:安装 phpunit

composer update phpunit/phpunit

第四步:运行测试

./vendor/bin/phpunit

强制更新(覆盖模板文件)

当包升级后,若需要将最新的 bootstrap.phpHttpTestCase.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.phptests/Feature/Controllers/UserControllerTest.php
app/common/service/OrderService.phptests/Unit/Services/OrderServiceTest.php
app/common/model/User.phptests/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-Typebody 编码
request('POST', ...)不设置表单($_POST
requestJson('POST', ...)application/jsonJSON(php://input
requestJson('PUT', ...)application/jsonJSON(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