如何用Array驱动Session模拟请求测试Laravel自定义Guard?
修复Laravel自定义Guard测试中的Session未设置错误
问题背景
需要测试自定义Laravel Guard,要求必须提供正确的email、password和user_id才能完成认证,缺少任一字段则无法通过认证。但执行测试时抛出Session store not set on request.错误。
错误信息
There was 1 error:
- Tests\Feature\Extensions\CustomerGuardTest::testAuthSuccess
RuntimeException: Session store not set on request./var/www/html/vendor/laravel/framework/src/Illuminate/Http/Request.php:560
/var/www/html/app/Extensions/CustomerGuard.php:13
/var/www/html/tests/Feature/Extensions/CustomGuardTest.php:25
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:61
相关代码
自定义Guard代码
namespace App\Extensions; use Illuminate\Auth\SessionGuard; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Http\Request; class CustomGuard extends SessionGuard { public function __construct($name, UserProvider $provider, Request $request) { parent::__construct($name, $provider, $request->session() ,$request); } public function attempt(array $credentials = [], $remember = false) { $user = $this->provider->retrieveByCredentials($credentials); // 检查用户是否存在且密码正确 if ($user && $this->provider->validateCredentials($user, $credentials)) { // 登录用户 $this->login($user, $remember); return true; } return false; } }
自定义Provider代码
namespace App\Extensions; use Illuminate\Auth\EloquentUserProvider; use Illuminate\Contracts\Auth\Authenticatable as UserContract; class CustomeProvider extends EloquentUserProvider { public function retrieveByCredentials(array $credentials) { if (empty($credentials) || (count($credentials) === 1 && array_key_exists('password', $credentials))) { return null; } // 根据email和user_id查询用户 return $this->createModel()->newQuery() ->where('email', $credentials['email']) ->where('user_id', $credentials['user_id']) ->first(); } public function validateCredentials(UserContract $customer, array $credentials) { if(isset($credentials['user_id']) && (int)$customer->user_id==$credentials['user_id']){ return parent::validateCredentials($customer,$credentials); } return false; } }
原错误测试代码
namespace Tests\Feature\Extensions; use App\Models\Customer; use App\Extensions\CustomerUser; use Illuminate\Http\Request; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Auth; use Mockery; use Tests\TestCase; class CustomGuardTest extends TestCase { use RefreshDatabase; public function testAuthSuccess(): void { // 默认密码为1234 $customer = CustomerUser::factory()->create(); $request = $this->app->make('request'); $guard = new CustomGuard('customer_session', Auth::createUserProvider('customer'), $request); $authenticated = $guard->attempt(['email' => $customer->email, 'password' => '1234', 'user_id' => $customer->user_id]); $this->assertTrue($authenticated); } }
解决方案:使用Array驱动模拟Session
要修复该错误,需手动为请求绑定一个Array驱动的Session实例,具体步骤如下:
修改后的测试代码
namespace Tests\Feature\Extensions; use App\Extensions\CustomerUser; use Illuminate\Http\Request; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Auth; use Illuminate\Session\ArraySessionStore; use Tests\TestCase; class CustomGuardTest extends TestCase { use RefreshDatabase; public function testAuthSuccess(): void { // 创建测试用户 $customer = CustomerUser::factory()->create(); // 初始化Array驱动的Session(内存存储,适合测试场景) $session = new ArraySessionStore; $request = Request::create('/', 'GET'); // 将Session绑定到请求 $request->setLaravelSession($session); // 覆盖请求的Session解析器,确保返回自定义的Session实例 $request->setSessionResolver(fn() => $session); // 实例化自定义Guard $guard = new \App\Extensions\CustomGuard( 'customer_session', Auth::createUserProvider('customer'), $request ); // 执行认证尝试 $authenticated = $guard->attempt([ 'email' => $customer->email, 'password' => '1234', 'user_id' => $customer->user_id ]); // 断言认证成功 $this->assertTrue($authenticated); } // 测试缺少user_id时认证失败 public function testAuthFailsWithoutUserId(): void { $customer = CustomerUser::factory()->create(); $session = new ArraySessionStore; $request = Request::create('/', 'GET'); $request->setLaravelSession($session); $request->setSessionResolver(fn() => $session); $guard = new \App\Extensions\CustomGuard( 'customer_session', Auth::createUserProvider('customer'), $request ); // 不传入user_id $authenticated = $guard->attempt([ 'email' => $customer->email, 'password' => '1234' ]); $this->assertFalse($authenticated); } }
关键说明
ArraySessionStore是Laravel提供的内存型Session驱动,无需依赖外部存储,适合单元/功能测试场景- 通过
setLaravelSession和setSessionResolver确保请求能正确获取到Session实例,彻底解决Session store not set on request错误 - 新增
testAuthFailsWithoutUserId测试用例,覆盖需求中"缺少字段无法认证"的边界逻辑
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

