You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Array驱动Session模拟请求测试Laravel自定义Guard?

修复Laravel自定义Guard测试中的Session未设置错误

问题背景

需要测试自定义Laravel Guard,要求必须提供正确的email、password和user_id才能完成认证,缺少任一字段则无法通过认证。但执行测试时抛出Session store not set on request.错误。

错误信息

There was 1 error:

  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:38:23