如何正确搭建贷款申请处理方法的测试场景?
解决测试中无法获取内部创建贷款记录ID的方案
核心问题
测试时,processLoanApplication方法内部才创建贷款记录,导致提前配置Http::fake时无法获取loan->id来填充API响应内容。以下是三种实用解决思路:
方案1:用闭包动态生成Fake响应
Http::fake支持通过闭包处理请求,在请求触发时(此时贷款记录已创建)动态获取loan_id并构造响应:
Http::fake([ config('services.dummy.url').'/v3/transfers' => function ($request) use ($user, $loanProvider, $ref) { // 从用户关联中查询刚创建的最新贷款记录 $loan = $user->loans()->latest()->first(); return Http::response([ 'status' => 'success', 'message' => 'test message', 'data' => [ 'tx_ref' => $ref, 'meta' => [ 'user_id' => $user->id, 'loan_id' => $loan->id, 'loanProvider_id' => $loanProvider->id, 'transfer_description' => 'Loan Disbursement', ], ], ], 200); }, config('services.dummy.url').'/v3/transactions/*/verify' => function ($request) use ($user) { $loan = $user->loans()->latest()->first(); return Http::response([ 'status' => 'success', 'message' => 'test message', 'meta' => [ 'loan_id' => $loan->id, ], ], 200); }, ]); // 执行测试方法 $this->actingAs($user)->post('/process-loan', $formData);
这种方式利用闭包延迟执行特性,在实际API请求触发时,贷款记录已经存在,直接查询即可拿到正确的loan_id。
方案2:监听Loan模型的创建事件
通过Laravel模型事件捕获内部创建的贷款实例,再在Fake中使用该实例的ID:
use App\Models\Loan; // 声明变量存储创建后的贷款实例 $createdLoan = null; // 监听Loan模型的created事件 Loan::created(function ($loan) use (&$createdLoan) { $createdLoan = $loan; }); // 配置Http Fake,使用捕获到的loan_id Http::fake([ config('services.dummy.url').'/v3/transfers' => function ($request) use ($user, $loanProvider, $ref, &$createdLoan) { return Http::response([ 'status' => 'success', 'message' => 'test message', 'data' => [ 'tx_ref' => $ref, 'meta' => [ 'user_id' => $user->id, 'loan_id' => $createdLoan->id, 'loanProvider_id' => $loanProvider->id, 'transfer_description' => 'Loan Disbursement', ], ], ], 200); }, config('services.dummy.url').'/v3/transactions/'.$ref.'/verify' => function ($request) use (&$createdLoan) { return Http::response([ 'status' => 'success', 'message' => 'test message', 'meta' => [ 'loan_id' => $createdLoan->id, ], ], 200); }, ]); // 执行测试方法 $this->actingAs($user)->post('/process-loan', $formData); // 后续断言 $this->assertNotNull($createdLoan); Http::assertSentCount(2);
事件监听能精准捕获方法内部创建的贷款实例,确保Fake响应中的loan_id完全匹配实际数据。
方案3:先执行方法,再验证API请求
如果测试重点是验证API请求是否携带正确参数,而非依赖响应中的loan_id,可以先执行业务方法,再通过Http::assertSent验证请求:
// 先伪造通用成功响应,无需提前知道loan_id Http::fake([ config('services.dummy.url').'/*' => Http::response([ 'status' => 'success', 'message' => 'test message', 'data' => ['tx_ref' => $ref], ], 200), ]); // 执行测试方法 $this->actingAs($user)->post('/process-loan', $formData); // 获取实际创建的贷款记录 $loan = $user->loans()->latest()->first(); // 验证第一个API请求的参数正确性 Http::assertSent(function ($request) use ($loan, $user, $loanProvider) { return $request->url() === config('services.dummy.url').'/v3/transfers' && $request['meta']['loan_id'] === $loan->id && $request['meta']['user_id'] === $user->id && $request['meta']['loanProvider_id'] === $loanProvider->id; }); // 验证第二个API请求的URL匹配 Http::assertSent(function ($request) use ($ref) { return str_ends_with($request->url(), "/v3/transactions/{$ref}/verify"); }); // 断言后续业务逻辑执行结果 $this->assertDatabaseHas('loans', [ 'id' => $loan->id, 'status' => 'disbursed' // 假设放款后状态更新为已放款 ]);
这种方式更关注请求的正确性,简化了Fake配置,适合验证流程完整性的测试场景。
额外建议:拆分测试关注点
将processLoanApplication的逻辑拆分为两个独立测试:
- 单独测试贷款创建:不调用API,仅验证表单数据是否正确存入数据库。
- 单独测试API调用与后续处理:手动创建Loan实例,传入
disburseLoan方法,测试API调用和转账记录、贷款状态更新逻辑。
拆分后每个测试的职责更清晰,也避免了内部创建资源带来的测试耦合问题。
内容的提问来源于stack exchange,提问作者Adefowowe
相关产品推荐
相关产品推荐

