Laravel自定义PHPUnit TestCase无法识别.env.testing问题排查
问题场景
我有一个Laravel 9(9.52.16,已在新版本测试)项目,采用自定义API认证流程:需先调用/init接口传入store_id和api_token获取init_token,验证通过后返回新token防止复用。项目分生产、测试两个数据库,stores表结构一致仅api_token不同,服务运行在WSL的Docker容器中(用Lando管理)。
已配置.env.testing连接测试库,phpunit.xml设置APP_ENV=testing等环境变量,自定义TestCaseCustom继承Laravel默认TestCase,测试中通过Http请求获取init_token。
执行php artisan test --testsuite=Unit --env=testing时,dd()显示当前连接测试库database_testing,但用测试库的api_token请求返回“Invalid api token”,改用生产库的api_token则正常。
已尝试:清除所有缓存、新增测试数据库连接配置、升级Laravel和PHPUnit版本、直接继承Illuminate测试基类、容器内执行测试、通过Lando命令执行测试。
可能原因
- HTTP测试请求未使用测试环境配置:若测试中用外部Http客户端(如Guzzle)直接访问容器真实端口,而非Laravel测试客户端(
$this->post()等),请求会命中运行中的生产环境应用实例,而非加载测试配置的测试环境逻辑。 - 认证逻辑硬编码数据库连接:
/init接口的认证代码若硬指定生产库连接(如DB::connection('mysql')),即使测试环境默认连接是测试库,认证仍会去生产库查询api_token。 - 配置缓存残留:Lando容器内可能存在缓存未彻底清除,导致测试命令未正确加载
.env.testing配置。 - 多环境实例冲突:测试时生产环境容器未停止,请求被路由至生产实例,导致生产token有效。
解决步骤
1. 改用Laravel测试客户端发起请求
放弃外部Http客户端,使用Laravel内置测试方法直接调用路由逻辑,确保使用测试环境配置:
// 在测试方法中 $response = $this->post('/init', [ 'store_id' => '测试库对应的store_id', 'api_token' => '测试库的api_token' ]); $response->assertStatus(200); $initToken = $response->json('init_token');
2. 检查认证逻辑的数据库连接
打开/init接口的控制器或中间件代码,确认查询使用默认连接而非硬编码:
// 错误示例:硬编码生产连接 $store = DB::connection('mysql')->where('api_token', $request->api_token)->first(); // 正确示例:使用默认连接(测试环境自动切换为测试库) $store = DB::table('stores')->where('api_token', $request->api_token)->first();
3. 确保测试环境配置正确加载
在phpunit.xml中明确覆盖数据库相关环境变量,避免配置冲突:
<php> <server name="APP_ENV" value="testing"/> <server name="DB_CONNECTION" value="mysql_testing"/> <server name="DB_DATABASE" value="database_testing"/> <server name="DB_HOST" value="数据库地址"/> <server name="DB_PORT" value="端口"/> <server name="DB_USERNAME" value="用户名"/> <server name="DB_PASSWORD" value="密码"/> </php>
同时保证.env.testing中的配置与phpunit.xml一致。
4. 彻底清除缓存并重启容器
进入Lando容器执行缓存清理命令,再重启容器:
lando ssh php artisan cache:clear php artisan config:clear php artisan route:clear php artisan view:clear exit lando restart
5. 停止生产环境实例
测试期间停止生产环境的应用容器,避免请求路由错误。
内容的提问来源于stack exchange,提问作者Alex

