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

如何在Express中用Jest+Supertest测试Passport认证请求

Passport.js 认证接口测试方案

两种写法失效的核心原因很明确:

  1. 直接取token塞query参数的写法完全不适配passport-local默认的session认证逻辑,服务端不会从url参数里读取认证凭证,自然识别不到登录状态。
  2. supertest-session 会话不生效基本是两个低级问题:一是发登录请求时没指定application/x-www-form-urlencoded格式,passport拿不到表单里的账号密码,实际根本没登录成功——你只判断了302状态码,但登录失败也会触发302跳转,等于拿着未认证的会话测受限接口;二是express-session开了secure cookie,但supertest发的是HTTP请求,cookie不会被持久化,会话自然断了。

下面给两种可直接运行的稳定方案,兼容当前所有新版本依赖。

方案1:原生Supertest 手动持久化Cookie(无额外依赖,最稳定)

不用额外装会话库,直接提取登录接口返回的cookie,后续请求手动携带即可,不存在版本兼容问题。

const request = require('supertest');
const app = require('../app');
const { pool } = require('../dbConfig');

// 测试账号
const testAccount = { email: 'loginTestUser@gmail.com', password: '123456' };
// 路由常量
const loginUrl = '/users/login';
const createRoomUrl = '/rooms/create';
const dashboardUrl = '/users/dashboard';

// 全局存登录后的cookie
let authCookie;

afterAll(async () => {
  await pool.end();
});

describe('Room CRUD: Create | POST /rooms/create', () => {
  // 所有用例跑之前先登录,拿有效cookie
  beforeAll(async () => {
    const loginRes = await request(app)
      .post(loginUrl)
      // 关键:必须设form格式,和前端登录请求的content-type一致
      .type('form')
      .send(testAccount);
    // 校验登录真的成功,不要只看302
    expect(loginRes.statusCode).toBe(302);
    expect(loginRes.headers.location).toBe(dashboardUrl);
    // 提取set-cookie头,存下来给后续请求用
    authCookie = loginRes.headers['set-cookie'];
  });

  test('已登录用户访问创建房间接口返回200', async () => {
    const res = await request(app)
      .get(createRoomUrl)
      // 带上登录拿到的cookie
      .set('Cookie', authCookie);
    expect(res.statusCode).toBe(200);
  });
});

方案2:修正后的supertest-session写法

如果偏好会话库的写法,补全缺失的配置和校验逻辑即可:

const session = require('supertest-session');
const myApp = require('../app');
const testAccount = { email: 'loginTestUser@gmail.com', password: '123456' };

let testSession;

beforeEach(() => {
  testSession = session(myApp);
});

test('未登录用户访问受限页面返回302跳转', (done) => {
  testSession.get('/rooms/create')
    .expect(302)
    .end(done);
});

describe('登录后访问受限接口', () => {
  let authenticatedSession;

  beforeEach((done) => {
    testSession.post('/users/login')
      // 关键1:必须指定form格式,否则passport读不到账号密码
      .type('form')
      .send(testAccount)
      .expect(302)
      // 关键2:校验跳转地址确实是登录成功后的dashboard,避免假登录
      .expect('Location', '/users/dashboard')
      .end((err) => {
        if (err) return done(err);
        authenticatedSession = testSession;
        return done();
      });
  });

  test('已登录用户访问创建房间页返回200', (done) => {
    authenticatedSession.get('/rooms/create')
      .expect(200)
      .end(done);
  });
});

排查清单(代码写完还是302就逐项核对)

  • express-session 配置按环境区分:测试环境下cookie.secure必须设为false,否则HTTP请求不会携带cookie,生产环境再开true即可,配置示例:
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000
  }
}));
  • 登录校验不要只判断302状态码,必须校验Location响应头是登录成功后的目标地址(比如dashboard路由),避免把登录失败的跳转误判为登录成功。
  • 确认测试库中确实存在预设的测试账号,密码哈希匹配逻辑正常,不要因为账号不存在、密码错误导致假登录。
  • 如果你实际用的是passport-jwt而不是session认证,就不要用cookie方案,登录后从响应体取token,后续请求放在Authorization请求头里,格式为Bearer ${token}。

内容的提问来源于stack exchange,提问作者NoobSailboat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:42:13