在Jest中,两个端点调用之间的会话中未持久化的信息

drkbr07n  于 9个月前  发布在  Jest
关注(0)|答案(2)|浏览(114)

我使用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);

  • 然而 * 当我使用supertest调用一个sign_in端点,然后调用另一个试图读取使用session持久化的信息的端点时,实际上没有任何信息。req.session正确地返回了一个session对象,但userId是null。如何使express-sessionJest中工作而不需要hacks和mocking任何东西?
bbuxkriu

bbuxkriu1#

首先,我们应该知道express-session会将会话ID存储在cookie中,并通过会话ID在内存存储(默认存储)中查找会话数据。因此,我们需要保存会话ID cookie并在以下请求中使用它。
supertest包使用了superagent,你不需要模仿任何东西。
我们应该使用superagent提供的request.agent()方法。来自Agents for global state文档:
在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请求,超级代理将调用Agent.prototype._attachCookies()方法将cookie从cookie jar示例附加到请求。

/**
 * 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;
};


例如,在一个示例中,

File 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;

File 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
----------|---------|----------|---------|---------|-------------------

mklgxw1f

mklgxw1f2#

您遇到此问题的原因是supertest为每个.get().post()等函数调用发出单独的HTTP请求,并且每个请求都是隔离的和无状态的。也就是说,它们之间不像真实的的Web浏览器那样共享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或mock,并且应该允许express-session在Jest测试中正确工作,

相关问题