Laravel接口测试:如何断言响应返回预期分页数据?
问题描述
我有如下Laravel路由:
Route::get('/records', function(Request $request) { $page = (int) $request->get('page'); $limit = (int) $request->get('limit'); return new JsonResponse(\App\Model\Record::paginate($limit, ['*'], '', $page), 200); });
同时编写了如下单元测试:
use App\Model\Record; use Tests\TestCase; class RecordsEndpointTest extends TestCase { public function testRecordsFetched() { $response = $this->get('/records?page=1&limit=10'); $response->assertStatus(200); Records::factory()->count(100)->create(); $expectedRecords = Records::paginate(10, ['*'], '', 1)->items(); foreach ($expectedRecords as $dbRecord) { $response->assertJson(function(AssertableJson $json) use ($dbRecord) { $json->has('data', 10, function(AssertableJson $json) use ($dbRecord) { $json->where('id', $dbRecord->id) ->where('name', $dbRecord->name) ->where('surname', $dbRecord->surname) ->where('birth_date', $dbRecord->birth_date) ->where('death_date', $dbRecord->death_date) ->where('agency', $dbRecord->agency) ->where('country_of_operation', $dbRecord->country_of_operation) ->etc(); }); }); } } }
运行测试时出现如下错误:
1) Tests\Feature\Routes\SpyRoutesTest::testGetSpiesNoFilters Unexpected properties were found on the root level. Failed asserting that two arrays are identical. --- Expected +++ Actual @@ @@ -Array &0 () +Array &0 ( + 0 => 'current_page' + 2 => 'first_page_url' + 3 => 'from' + 4 => 'last_page' + 5 => 'last_page_url' + 6 => 'links' + 7 => 'next_page_url' + 8 => 'path' + 9 => 'per_page' + 10 => 'prev_page_url' + 11 => 'to' + 12 => 'total' +) /var/www/html/vendor/laravel/framework/src/Illuminate/Testing/Fluent/Concerns/Interaction.php:44 /var/www/html/vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:648 /var/www/html/tests/Feature/Routes/SpyRoutesTest.php:213 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:174
我想要验证响应JSON的data部分数据与分页查询返回的结果一致,请问该如何实现?
解决方案
1. 修正测试执行顺序
当前测试是先请求接口,再创建测试数据,这会导致接口返回空数据,完全不符合预期。必须先创建数据,再发起请求。
2. 正确处理分页字段的断言
错误提示的核心原因是:你在assertJson闭包里没有声明允许存在的顶层分页字段(比如current_page、total等),Laravel的断言会默认检查顶层字段是否完全匹配预期。另外,循环验证每条数据的写法效率低下,直接针对data数组做批量验证更合理。
修改后的测试代码如下:
use App\Model\Record; use Tests\TestCase; use Illuminate\Testing\Fluent\AssertableJson; class RecordsEndpointTest extends TestCase { public function testRecordsFetched() { // 1. 先创建测试数据 Record::factory()->count(100)->create(); // 2. 发起请求 $response = $this->get('/records?page=1&limit=10'); $response->assertStatus(200); // 3. 获取预期的分页数据集合 $expectedRecords = Record::paginate(10, ['*'], '', 1)->items(); // 4. 验证响应结构和数据 $response->assertJson(function(AssertableJson $json) use ($expectedRecords) { // 声明允许存在的顶层分页字段 $json->hasAll(['current_page', 'data', 'total', 'per_page']) ->where('current_page', 1) ->where('per_page', 10) ->where('total', 100) // 验证data数组的长度 ->has('data', 10) // 遍历验证data数组中的每条数据 ->each('data', function(AssertableJson $item) use ($expectedRecords) { $matchingRecord = collect($expectedRecords)->firstWhere('id', $item->id); $item->where('id', $matchingRecord->id) ->where('name', $matchingRecord->name) ->where('surname', $matchingRecord->surname) ->where('birth_date', $matchingRecord->birth_date) ->where('death_date', $matchingRecord->death_date) ->where('agency', $matchingRecord->agency) ->where('country_of_operation', $matchingRecord->country_of_operation) ->etc(); }); }); } }
3. 简化验证的替代方案
如果只需要快速验证data部分和预期数据完全一致,可以直接将预期数据转成数组后断言:
// 前面步骤不变 $expectedData = Record::paginate(10, ['*'], '', 1)->toArray()['data']; // 直接断言data部分匹配 $response->assertJsonFragment(['data' => $expectedData]);
或者更直接地验证完整结构:
$response->assertJson([ 'current_page' => 1, 'per_page' => 10, 'total' => 100, 'data' => $expectedData, ]);
关键注意点
- 测试数据必须在请求前创建,否则接口会返回空数据
- 使用
hasAll声明允许存在的顶层分页字段,避免“意外属性”错误 - 用
each方法遍历data数组验证每条数据,比循环调用assertJson更高效
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

