Laravel PHPUnit测试:如何触发异常并测试控制器捕获逻辑
测试Laravel控制器store方法的异常处理catch块
问题背景
需要测试控制器store方法中捕获数据库交互异常的catch代码块,确保异常发生时能正确记录日志并返回重定向。
控制器代码
public function store(Request $request, Pipeline $pipeline) { try { $request->validate([ "name" => ["required", "max:512"], "stage" => ["required", "in:".Stage::toCSV()], ]); // 数据库交互逻辑(例如 MyModel::create()) return response()->json(["message" => trans("round.created")], 201); } catch (\Exception $e) { Log::error('Exception in MyController@store: ' . $e->getMessage()); Log::error('Stack trace: ' . $e->getTraceAsString()); return redirect()->back()->banner(trans('error')); } }
现有尝试的问题
之前的两种mock方式存在误区:
- 未确保请求通过验证,导致代码根本没走到数据库交互环节就提前终止
- mock
Exception类完全没必要,反而会干扰正常的异常抛出逻辑
正确解决方案
核心要点
- 必须发送符合验证规则的请求数据,否则会在
validate阶段提前抛出异常,无法进入catch块 - 仅mock模型的
create静态方法并抛出异常,无需mockException类 - 通过断言重定向、会话消息(可选断言日志)验证catch块的执行逻辑
完整测试代码示例
public function test_store_handles_database_exception() { // 1. 准备通过验证的请求数据 $validRequestData = [ 'name' => '测试名称', 'stage' => Stage::YOUR_VALID_STAGE, // 替换为符合Stage枚举的有效值 ]; // 2. Mock模型的create方法,抛出异常 $this->partialMock(\App\Models\MyModel::class, function ($mock) use ($validRequestData) { $mock->shouldReceive('create') ->once() ->with($validRequestData) // 可选:验证传入参数的正确性 ->andThrow(new \Exception('模拟数据库异常')); }); // 3. 可选:禁用真实日志写入,仅断言日志调用次数 \Illuminate\Support\Facades\Log::shouldReceive('error')->twice(); // 4. 发送POST请求到store路由 $response = $this->post(route('your.store.route.name'), $validRequestData); // 5. 断言返回重定向 $response->assertRedirect(); // 断言会话中存在指定的banner错误消息 $response->assertSessionHas('banner', trans('error')); }
关键细节解释
partialMock:仅mock指定的create方法,模型的其他方法保持原有逻辑,避免影响其他潜在调用- 请求数据必须符合验证规则:确保代码执行到数据库交互环节,才能触发我们模拟的异常
- 无需mock
Exception:直接抛出真实的Exception实例,就能正常触发catch块中的日志记录和返回逻辑
内容的提问来源于stack exchange,提问作者Yashgupta
相关产品推荐
相关产品推荐

