如何在测试中通过Eloquent ORM模拟记录插入及数据库错误?
Laravel 控制器测试相关问题解答
问题代码
控制器代码
namespace App\Http\Controllers; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Routing\Controller as BaseController; use App\Models\Record; class SpyController extends BaseController { public function add(Request $request) { $model = new Record(); $model->value = $request->get('value'); return new JsonResponse(['message'=>'Insert Success','record'=>$model],201); } }
测试用例代码
namespace Tests\Controller; use Illuminate\Testing\Fluent\AssertableJson; use Laravel\Sanctum\Sanctum; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\DatabaseTestCase; class SpyRoutesTest extends TestCase { public function addSuccess() { DB::fake() $response = $this->put('/record',['value'=>'lalalala']); $this->assertStatus(201); } }
问题列表
- 如何模拟真实的记录插入操作?
DB::fake()是否默认判定插入成功?- 如何模拟数据库连接超时这类错误?
解答
1. 模拟真实记录插入操作
首先要修正控制器代码:当前控制器仅创建了Record模型实例,但未执行插入数据库的操作(缺少$model->save()或Record::create()逻辑),所以无论测试怎么写,都不会有真实插入行为。
模拟真实插入有两种可行方式:
方式一:使用真实数据库测试
在测试类中引入RefreshDatabasetrait,它会在每个测试前自动迁移数据库,测试后重置数据,保证每次测试环境干净。同时移除DB::fake(),让测试真正执行数据库插入,之后可断言数据库中是否存在目标记录:class SpyRoutesTest extends TestCase { use RefreshDatabase; public function testAddSuccess() { $response = $this->post('/record', ['value' => 'lalalala']); $response->assertStatus(201); // 断言数据库中存在指定记录 $this->assertDatabaseHas('records', ['value' => 'lalalala']); } }注意:测试方法名必须以
test开头,否则Laravel无法识别为测试用例;新增记录的请求通常用POST而非PUT,需确保路由配置与请求方法对应。方式二:用
DB::fake()模拟并断言操作
若不想依赖真实数据库,DB::fake()可拦截数据库操作,你可以断言插入操作是否按预期执行,同时确保控制器内真正触发插入逻辑:public function testAddSuccess() { DB::fake(); $response = $this->post('/record', ['value' => 'lalalala']); $response->assertStatus(201); // 断言records表执行了一次插入,且数据匹配 DB::assertInserted('records', [ 'value' => 'lalalala' ]); }
2. DB::fake()是否默认判定插入成功?
是的,DB::fake()默认会模拟所有数据库操作成功完成,不会主动抛出错误。它的核心作用是拦截真实数据库查询、记录操作执行情况,方便你断言操作是否符合预期,但不会自动模拟失败场景,除非手动配置异常抛出逻辑。
3. 模拟数据库连接超时这类错误
可以通过DB::shouldReceive()或模型的Mock方法,模拟数据库操作抛出PDOException(连接超时通常属于这类异常):
public function testAddWithDatabaseTimeout() { // 模拟Record模型的save方法抛出连接超时异常 Record::shouldReceive('save') ->once() ->andThrow(new \PDOException('SQLSTATE[HY000] [2002] Connection timed out')); $response = $this->post('/record', ['value' => 'lalalala']); // 断言返回自定义的错误状态码 $response->assertStatus(500); // 可额外断言返回的错误信息 $response->assertJson(['message' => '数据库连接失败']); }
如果要模拟更底层的数据库连接错误,也可以直接针对DB facade的连接方法做Mock:
DB::shouldReceive('connection') ->once() ->andThrow(new \PDOException('Connection timed out'));
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

