Laravel中使用Paginator对象测试时的断言失败问题
Laravel控制器测试中assertViewHas验证分页器对象失败的解决方案
问题场景
控制器代码:
public function index() { $title = "Our list of genres"; $genres = Genre::paginate(20); return view('lists-view.genres-list', compact('genres', 'title')); }
对应的测试代码:
// 填充数据库 (new Genre())::factory(20)->create(); // 获取20条分类数据 $genres = Genre::paginate(20); $response = $this->get('/genres'); $response->assertStatus(200); $response->assertSee("Our list of genres"); $response->assertViewHas('title', "Our list of genres"); $response->assertViewHas('genres',$genres);
执行测试时,$response->assertViewHas('genres',$genres);断言失败,错误信息显示两个分页器对象的path和options中的path不一致:
Failed asserting that two objects are equal. --- Expected +++ Actual @@ @@ 'items' => Illuminate\Database\Eloquent\Collection Object (...) 'perPage' => 20 'currentPage' => 1 - 'path' => 'http://books-store.loc' + 'path' => 'http://books-store.loc/genres' 'query' => Array () 'fragment' => null 'pageName' => 'page' 'onEachSide' => 3 'options' => Array ( - 'path' => 'http://books-store.loc' + 'path' => 'http://books-store.loc/genres' 'pageName' => 'page' ) 'total' => 20 'lastPage' => 1 )
原因分析
控制器中调用paginate()时,Laravel会自动根据当前HTTP请求的URL(即/genres)设置分页器的path属性;而测试中手动创建的$genres分页器对象,是基于CLI环境的默认域名(无请求路径)生成的,两者的path属性自然不一致,导致对象全等断言失败。
解决方案
方案一:验证分页器核心数据(推荐)
测试的核心是确认传递给视图的分页数据正确,而非分页器对象的所有属性(比如URL路径这类和请求上下文绑定的属性)。可以使用闭包形式的assertViewHas,只验证关键数据:
$response->assertViewHas('genres', function ($paginator) { // 验证总条数、每页条数、当前页、数据数量 return $paginator->total() === 20 && $paginator->perPage() === 20 && $paginator->currentPage() === 1 && $paginator->items()->count() === 20; });
如果需要更细致验证数据内容,可以对比数据的标识字段(比如id):
$response->assertViewHas('genres', function ($paginator) { $expectedIds = Genre::all()->pluck('id'); return $paginator->items()->pluck('id')->equals($expectedIds) && $paginator->total() === 20; });
方案二:同步测试中分页器的请求路径
如果一定要断言整个分页器对象,可以手动设置测试中分页器的请求路径,使其和控制器中的一致:
// 填充数据库 (new Genre())::factory(20)->create(); // 先模拟请求上下文,让paginate()自动获取正确的path $this->get('/genres'); $genres = Genre::paginate(20); // 再次发起请求并断言 $response = $this->get('/genres'); // ...其他断言 $response->assertViewHas('genres', $genres);
或者直接设置请求上下文的路径:
// 填充数据库 (new Genre())::factory(20)->create(); // 设置当前请求的路径 app('request')->setPathInfo('/genres'); $genres = Genre::paginate(20); $response = $this->get('/genres'); // ...其他断言 $response->assertViewHas('genres', $genres);
总结
无需强行修改分页器的options属性,因为URL路径属于请求上下文相关的细节,并非业务测试的核心。使用闭包验证分页器的核心数据,既能确保视图获取到正确的分页数据,也能让测试更健壮、聚焦业务逻辑。
内容的提问来源于stack exchange,提问作者AlexZelenskiy
相关产品推荐
相关产品推荐

