Jest 中两个端点调用之间的会话中的信息未保留

问题描述 投票:0回答:2

我正在使用 Express-Session 以最简单的方式存储登录信息:

req.session.userId = user.id

我将此配置用于一切正常的开发环境和规格:

app.use(express.json())
app.use(sessions({
    secret: "secretthatwillbeputinenv",
    saveUninitialized: true,
    cookie: { maxAge: 1000 * 60 * 60 * 24 },
    resave: false
}));
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
app.use(cors())
app.use("/", router);

但是当我使用超级测试调用sign_in端点,然后调用另一个尝试读取使用会话保存的信息的端点时,实际上没有任何信息。

req.session
正确返回会话对象,但
userId
为空。 如何让
express-session
Jest 中工作而无需黑客攻击且不嘲笑任何东西?

node.js express jestjs express-session supertest
2个回答
0
投票

首先,我们应该知道

express-session
会将Session ID存储在cookie中,并通过Session ID在内存存储(默认存储)中查找会话数据。所以我们需要保存会话ID cookie并在接下来的请求中使用它。

supertest
包底层使用
superagent
。你不需要嘲笑任何东西。

我们应该使用request.agent()

提供的
superagent
方法。来自全局状态代理文档:

在 Node 中 SuperAgent 默认不保存 cookie,但是你可以使用

.agent()
方法创建保存 cookie 的 SuperAgent 副本

request.agent()
将创建一个 cookie jar 实例(
CookieJar
类来自
cookiejar
包)。当 HTTP 响应时,它会调用
Agent.prototype._saveCookies()
方法将 cookie 保存到 cookie jar 实例中。

/**
 * Save the cookies in the given `res` to
 * the agent's cookie jar for persistence.
 *
 * @param {Response} res
 * @api private
 */

Agent.prototype._saveCookies = function (res) {
  const cookies = res.headers['set-cookie'];
  if (cookies) {
    const url = parse(res.request?.url || '')
    this.jar.setCookies(cookies, url.hostname, url.pathname);
  }
};

之后,发送一个新的 HTTP 请求,superagent 将调用

Agent.prototype._attachCookies()
方法将 cookie jar 实例中的 cookie 附加到请求中。

/**
 * Attach cookies when available to the given `req`.
 *
 * @param {Request} req
 * @api private
 */

Agent.prototype._attachCookies = function (request_) {
  const url = parse(request_.url);
  const access = new CookieAccessInfo(
    url.hostname,
    url.pathname,
    url.protocol === 'https:'
  );
  const cookies = this.jar.getCookies(access).toValueString();
  request_.cookies = cookies;
};

例如

app.js

const express = require('express');
const sessions = require('express-session');

const app = express();

app.use(sessions({
  secret: "secretthatwillbeputinenv",
  saveUninitialized: true,
  cookie: { maxAge: 1000 * 60 * 60 * 24 },
  resave: false
}));
app.get('/', (req, res) => {
  req.session.userId = 1;
  res.sendStatus(200);
})

app.get('/return', (req, res) => {
  console.log('userId: ', req.session.userId);
  if (req.session.userId) return res.json({ userId: req.session.userId });
  res.sendStatus(500)
})

module.exports = app;

app.test.js

const request = require('supertest');
const { expect } = require('chai');
const app = require('./app');

const agent = request.agent(app);

describe('76401408', () => {
  it('should pass', async () => {
    const res1 = await agent.get('/');
    expect(res1.headers["set-cookie"]).to.be.match(/connect.sid=/);
    const res2 = await agent.get('/return')
    expect(res2.body).to.be.eql({ userId: 1 })
  });
});

测试结果:

  76401408
userId:  1
    ✓ should pass (101ms)


  1 passing (115ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   92.31 |       50 |     100 |   91.67 |                   
 app.js   |   92.31 |       50 |     100 |   91.67 | 20                
----------|---------|----------|---------|---------|-------------------

-1
投票

您遇到此问题的原因是

supertest
为每个
.get()
.post()
等函数调用发出单独的HTTP请求,并且每个请求都是隔离且无状态的。也就是说,它们之间不会共享 cookie 或会话,就像真正的网络浏览器那样。

supertest
提供了一种“链接”请求的方式,可以保留cookie并可以维持会话。它涉及创建一个用于发出请求的
agent
实例。

你可以这样做:

const request = require('supertest');
const app = require('../app');  // your express app

const agent = request.agent(app);

describe('my test', () => {
  it('should login and then access a protected route', async () => {
    await agent
      .post('/login')
      .send({ username: 'user', password: 'pass' })
      .expect(200);

    await agent
      .get('/protected')
      .expect(200);
  });
});

request.agent(app)
创建一个
supertest
代理。该代理将 cookie 附加到它发出的请求,因此它可以在多个请求之间维护会话。

.post('/login')
呼叫记录在用户中。因为这是通过代理完成的,所以会保留登录会话。

.get('/protected')
呼叫访问受保护的路由。因为这也是使用
agent
完成的,所以它使用之前登录调用创建的会话。

此方法不需要任何 hack 或模拟,并且应该允许 Express-Session 在您的 Jest 测试中正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.