在 laravel 中快速启用 pest 测试只需运行 php artisan pest:install,该命令自动创建配置、注册插件、生成示例测试并替换配置入口;需确保 laravel ≥9.27 且已安装 pestphp/pest 和 pestphp/pest-plugin-laravel。

怎么在 Laravel 里快速启用 Pest 测试
Pest 是 Laravel 官方推荐的测试框架,本质是 PHPUnit 的轻量封装,但语法更简洁。它不是独立安装的“新东西”,而是通过 pestphp/pest + pestphp/pest-plugin-laravel 插件与 Laravel 深度集成。直接用 php artisan pest:install 就能完成初始化,这命令会:自动创建 tests/Pest.php 入口、注册 Laravel 插件、生成示例测试文件、并把 phpunit.xml 替换为 pest.php 配置入口。
常见错误现象:php artisan pest:install 报错 “Command not found”——说明你没装 laravel/pint 或 Pest 包本身未被正确加载;或者 Laravel 版本低于 9.27(Pest 插件支持起点)。别手动改 composer.json 加包再 require-dev,先确认 Laravel 版本,再执行安装命令。
- 确保已运行
composer require --dev pestphp/pest pestphp/pest-plugin-laravel - 运行
php artisan pest:install后,tests/Feature/ExampleTest.php会被自动转成tests/Feature/ExampleTest.php→ 实际是tests/Feature/ExampleTest.php被删,新建tests/Feature/ExampleTest.php并用test()函数重写 - 首次运行
./vendor/bin/pest失败?检查tests/Pest.php是否存在,且是否包含uses(Tests\TestCase::class)->in('Feature', 'Unit');
test() 和 it() 怎么选,参数和作用域有啥区别
test() 和 it() 都是 Pest 提供的顶层测试定义函数,语义上没强制区别,但社区习惯用 it() 写单个断言场景,test() 写多步骤逻辑。它们底层都调用 PHPUnit 的 TestCase::assertTrue() 等方法,但关键差异在闭包执行时的上下文:
-
test('creates user', function () { ... }):闭包内可直接访问$this->get()、$this->postJson()等 Laravel 测试辅助方法,因为 Pest 自动绑定了TestCase实例 -
it('redirects when not authenticated', function () { ... }):行为完全一致,只是命名更聚焦“行为描述” - 如果闭包里用了
expect()(比如expect($response)->status(302)),必须确保已启用pest-plugin-assertions,否则报Call to undefined function expect() - 别在
test()闭包里 return 值——Pest 不捕获返回值,也不会自动断言;所有校验必须显式调用expect()或$this->assert...
数据库迁移和事务回滚为什么有时不生效
Pest 默认复用 Laravel 的 TestCase,所以 RefreshDatabase trait 依然有效,但容易被忽略的是:它只在继承 Tests\TestCase 的测试类中起作用。而 Pest 的 test() 或 it() 是函数式写法,不显式声明 class,所以依赖 trait 的自动注入机制——这个机制靠 uses() 函数配置。
常见错误现象:测试里插入数据,下个测试还能查到;或 artisan migrate:fresh 被反复执行拖慢速度。根本原因是你没在 tests/Pest.php 中正确配置 uses(),或在单个测试里误用了 DatabaseMigrations(它每次跑都重跑全部 migration,比 RefreshDatabase 慢得多)。
- 确保
tests/Pest.php包含uses(Tests\TestCase::class, RefreshDatabase::class)->in('Feature'); - 不要在
it()里手动调用$this->artisan('migrate:fresh')——这会绕过事务控制,且破坏并发测试隔离性 - 单元测试(
Unit目录)默认不启用数据库 trait,如需操作模型,得显式加uses(RefreshDatabase::class)到该测试文件顶部 - SQLite 内存数据库(
:memory:)在 Pest 中默认启用,但如果你改了DB_CONNECTION为mysql,就得确保测试数据库允许无密码连接,否则RefreshDatabase会卡住
怎么测 API 返回结构、状态码和认证失败场景
Laravel 的 HTTP 测试方法($this->get(), $this->postJson())在 Pest 中完全可用,但新手常卡在响应断言写法上:直接 dd($response) 看不到 JSON body,或用 assertJson() 时路径写错导致断言失败却没报具体哪条 key 缺失。
- 用
$response = $this->postJson('/api/users', [...])后,立刻接$response->assertStatus(201)——这是最稳的状态码断言方式,比assertSuccessful()更明确 - 校验 JSON 结构优先用
$response->assertJson(['name' => 'John']),而不是json_decode($response->getContent(), true)手动取值——后者绕过了 Pest 的可读性增强(比如出错时会高亮显示 diff) - 测试未登录访问 API:先不调用
$this->actingAs(...),直接发请求,再用$response->assertStatus(401)或$response->assertJsonMissing(['user' => [...]]) - 注意
assertJsonPath()和assertJsonFragment()的区别:前者要求完整匹配路径(如'data.0.name'),后者只要 JSON body 中存在该键值对子集即可
真正难的不是写法,是测试边界——比如同时验证 token 过期、IP 限流、字段长度超限这三个条件叠加时的响应,这时候单个 it() 很容易变成“大杂烩”。拆成三个独立测试,每个只动一个变量,不然失败时根本分不清是认证逻辑错了,还是验证规则错了,还是中间件顺序乱了。










