如何在Express中用Jest+Supertest测试Passport认证请求
Passport.js 认证接口测试方案
两种写法失效的核心原因很明确:
- 直接取token塞query参数的写法完全不适配passport-local默认的session认证逻辑,服务端不会从url参数里读取认证凭证,自然识别不到登录状态。
- 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
相关产品推荐
相关产品推荐

