如何在摩卡测试套件中的服务器上使用存根函数



在快速服务器app.js上测试端点/allowUser2时,我正在尝试存根auth.session

//--auth.js--
module.exports.session = (req, res, next) => {
req.user = null;
next();
};
//--app.js--
const express = require('express');
const auth = require('./auth');
const app = express();
app.use(auth.session);
app.get('/allowUser2', (req, res) => {
if (!req.user) return res.status(401).send();
if (req.user.user === 2) return res.status(200).send();
});
app.listen(4001).on('listening', () => {
console.log(`HTTP server listening on port 4001`);
});
module.exports = app;

如果我的测试套件中只test1.js了这个测试文件,auth就会成功存根。

//--test1.js--
let app;
const sinon = require('sinon');
const auth = require('../../auth.js');
const chai = require('chai');
const chaiHttp = require('chai-http');
const { expect } = chai;
chai.use(chaiHttp);
let agent;
describe('should allow access', () => {
before(async () => {
// delete require.cache[require.resolve('../../app.js')]; // causes Error: listen EADDRINUSE: address already in use
sinon.stub(auth, 'session').callsFake((req, res, next) => {
req.user = { user: 1 };
next();
});
app = require('../../app.js');
agent = chai.request.agent(app);
});
after(async () => {
auth.session.restore();
});
it('should not allow access', async function () {
const response = await agent.get('/allowUser2');
expect(response.status).to.be.equal(200);
});
});

但是,如果我有多个需要app.js的测试文件,那么我就有问题了。如果在另一个测试文件中已经需要app.js,例如下面的test2.js,则 node 在test1.js中再次需要时不会重新加载app.js。这会导致app.js调用旧的auth.session函数,而不是新的存根函数。因此,用户未通过身份验证,测试失败。

//--test2.js--
const chai = require('chai');
const chaiHttp = require('chai-http');
const app = require('../../app.js');
const { expect } = chai;
chai.use(chaiHttp);
const agent = chai.request.agent(app);
describe('route /allowUser2', () => {
it("shouldn't allow access", async function () {
const response = await agent.get('/allowUser2');
expect(response.status).to.be.equal(401);
});
});

我尝试使用delete require.cache[require.resolve('../../app.js')];重新加载app.js.这在使用普通函数重新加载文件时有效,但是当文件是像app.js这样的服务器时,这会导致错误:Error: listen EADDRINUSE: address already in use.

重建:

  1. 下载存储库
  2. npm i
  3. npm test

如何在服务器上存根函数?

一种解决方案是将app.js转换为一个函数,该函数根据作为参数传入的端口号启动服务器。然后在需要时随机更改端口。我不喜欢此选项,因为可能有某种原因将应用程序保留在特定端口上。

app.js

const express = require('express');
const auth = require('./auth');
module.exports = (port) => {
const app = express();
app.use(auth.session);
app.get('/allowUser2', (req, res) => {
if (!req.user) return res.status(401).send();
if (req.user.user === 2) return res.status(200).send();
});
app.listen(port).on('listening', () => {
console.log(`HTTP server listening on port ${port}`);
});
return app;
};

当需要时

app = require('../../app.js')((Math.random() * 10000).toString().slice(0, 4));

我导出一个启动服务器并返回服务器实例和应用程序的函数,而不是导出app.js中的app。通过导出服务器实例,我可以关闭服务器。需要该应用程序才能传递到柴中。确保const app = express();在此函数中,而不是在它之前,否则它不会重新创建。

const express = require('express');
const auth = require('./auth');
const port = 4000;
module.exports = () => {
const app = express();
app.use(auth.session);
app.get('/allowUser2', (req, res) => {
if (!req.user) return res.status(401).send();
if (req.user.user === 2) return res.status(200).send();
});
app.post('/allowUser2', (req, res) => {
if (!req.user) return res.status(401).send();
if (req.user.user === 2) return res.status(200).send();
});
return {
server: app.listen(port).on('listening', () => {
console.log(`HTTP server listening on port ${port}`);
}),
app,
};
};

然后,在我的测试中,我可以在before中启动服务器,并在两个测试中after关闭服务器。

let app;
const sinon = require('sinon');
const auth = require('../../auth.js');
const chai = require('chai');
const chaiHttp = require('chai-http');
const { expect } = chai;
chai.use(chaiHttp);
let server;
describe('route /allowUser2', () => {
before(async () => {
// delete require.cache[require.resolve('../../app.js')]; // causes an error: `Error: listen EADDRINUSE: address already in use`.
sinon.stub(auth, 'session').callsFake((req, res, next) => {
req.user = { user: 2 };
next();
});
server = require('../../app.js')();
agent = chai.request.agent(server.app);
});
after(async () => {
server.server.close(() => {
console.log('Http server closed.');
});
auth.session.restore();
});
it('should allow access', async function () {
const response = await agent.get('/allowUser2');
expect(response.status).to.be.equal(200);
});
});

const chai = require('chai');
const chaiHttp = require('chai-http');
const { expect } = chai;
chai.use(chaiHttp);
let server;
let agent;
describe('route /allowUser2', () => {
before(async () => {
server = require('../../app.js')();
agent = chai.request.agent(server.app);
});
after(async () => {
server.server.close(() => {
console.log('Http server closed.');
});
});
it("shouldn't allow access", async function () {
const response = await agent.get('/allowUser2');
expect(response.status).to.be.equal(401);
});
});

工作回购

更新:建议的解决方案 https://github.com/DashBarkHuss/mocha_stub_server/pull/1

一个问题是您在应用程序中使用直接方法引用的方式.js阻止 Sinon 工作。 https://gist.github.com/corlaez/12382f97b706c964c24c6e70b45a4991

另一个问题(使用中的地址)是因为每次我们想要获取对应用程序的引用时,我们都在尝试在同一端口中创建服务器。将该应用/服务器创建分解为一个单独的步骤可以缓解该问题。

相关内容

最新更新