如何编写测试验证Laravel模型事务方法异常时进入catch触发回滚
解决方案
错误原因排查
你遇到的BadMethodCallException报错,核心原因是你错误地Mock了目标测试方法addRecord所属的MyModel全类,导致原类的addRecord静态方法被Mock覆盖不存在了。你需要测试的就是addRecord本身的逻辑,因此不要Mock这个方法,仅需要Mock它依赖的create方法和DB事务操作即可。
另外先修正你的业务代码笔误:catch块捕获的异常变量是$ex,你抛出的是未定义的$e,需要改为:
catch (\Exception $ex) { DB::rollback(); throw $ex; }
测试代码实现
基于Laravel官方测试规范,你可以通过Facade监听、模型静态方法Mock来实现需求:
public function test_add_record_rollback_on_exception() { // 监听DB事务操作,断言调用符合预期 \DB::shouldReceive('beginTransaction')->once(); \DB::shouldReceive('rollback')->once(); // 核心断言:失败时rollback必须被调用 \DB::shouldReceive('commit')->never(); // 核心断言:失败时commit绝对不能被调用 // Mock MyModel的create方法,调用时直接抛出异常,模拟try块失败场景 \App\Models\MyModel::shouldReceive('create') ->once() ->andThrow(new \Exception('模拟create失败')); // 断言方法会正常抛出异常 $this->expectException(\Exception::class); // 执行实际要测试的业务方法 \App\Models\MyModel::addRecord(); }
逻辑说明
- 我们只Mock
MyModel::create这个依赖方法,保留原addRecord的所有逻辑,确保测试的是真实业务流程 - 通过DB Facade的调用断言,直接验证事务回滚逻辑是否触发,完全匹配你的测试需求
- 不需要额外处理事务上下文,Mock的逻辑完全隔离了数据库操作,测试可以快速运行
内容的提问来源于stack exchange,提问作者cnx
相关产品推荐
相关产品推荐

