refreshdatabase比databasetransactions更适合大多数功能测试,因为它在每个测试方法前重置整个数据库schema,确保干净起点;而databasetransactions仅事务包裹,遇ddl或truncate即失效导致数据残留。

数据库测试在 Laravel 里不是“连上就行”,关键是要隔离、可重放、不污染环境——直接用 RefreshDatabase trait 是最稳的起点,别手写迁移或手动清表。
为什么 RefreshDatabase 比 DatabaseTransactions 更适合大多数功能测试
它会在每个测试方法执行前,把整个测试数据库(默认 sqlite:memory:)重新 migrate 一遍,确保从干净 schema 开始;而 DatabaseTransactions 只 wrap 在事务里,一旦测试中用了 DB::statement('TRUNCATE') 或触发了 DDL(如 CREATE TABLE),事务就失效,数据会残留。
- 默认只对
tests/Feature/下的测试生效,tests/Unit/不自动加载——单元测试里要用就得显式use RefreshDatabase; - 它依赖
phpunit.xml中的DB_CONNECTION=sqlite和DB_DATABASE=:memory:,如果改成mysql,就会真连本地 MySQL,跑得慢还可能冲突 - 如果测试里调用了 Artisan 命令(比如
Artisan::call('db:seed')),RefreshDatabase仍能保证每次重置,但 seed 逻辑必须幂等
DB::connection()->getPdo() 是连通性验证的唯一轻量方式
它不执行 SQL,只建立底层 PDO 连接。失败时抛出的错误(如 SQLSTATE[HY000] [2002] Connection refused)直接指向网络、配置或权限问题,而不是业务逻辑。
- 必须包在
try/catch里,否则未捕获异常会让整个测试中断 - 读写分离场景下,要分别测:
DB::connection('write')->getPdo()和DB::connection('read')->getPdo() - 在部署钩子或健康检查接口里用完记得调
DB::disconnect(),否则短生命周期容器容易耗尽连接数
工厂数据 + 断言组合才是真实数据库测试的核心
光有 clean DB 不够,还得让数据可预测、可验证。Laravel 的工厂(User::factory())和数据库断言(assertDatabaseHas())是配套使用的。
-
User::factory()->count(3)->create()比手写DB::table('users')->insert(...)更可靠,因为会走模型逻辑(含软删除、事件、casts 等) -
assertDatabaseMissing('users', ['email' => 'test@example.com'])失败?先确认是否真用了RefreshDatabase—— 如果没生效,上一个测试留下的数据会让这个断言永远过不了 - 避免在测试里用
$user->save()后立刻查库验证:Eloquent 默认不会 reload,要用$user->refresh()或User::find($user->id)
最容易被忽略的是环境一致性:.env 修改后没运行 php artisan config:clear,会导致 tinker 或测试里读到旧配置;phpunit.xml 里漏掉 APP_ENV=testing,则 APP_KEY 缺失会让加密相关测试直接 500。这些不是代码问题,而是执行上下文没对齐。











