作为 node express 中间件被跳过的 Sinon 存根
Sinon stub being skipped as node express middleware
我正在尝试测试特定路线的行为。即使我创建存根,它也会继续 运行 中间件。我希望事件认证暂时通过。我知道目前这不是真正的 "unit" 测试。我快到那里了。我还稍微简化了代码。这是要测试的代码:
const { rejectUnauthenticated } = require('../modules/event-authentication.middleware');
router.get('/event', rejectUnauthenticated, (req, res) => {
res.sendStatus(200);
});
这是我试图跳过的中间件:
const rejectUnauthenticated = async (req, res, next) => {
const { secretKey } = req.query;
if (secretKey) {
next();
} else {
res.status(403).send('Forbidden. Must include Secret Key for Event.');
}
};
module.exports = {
rejectUnauthenticated,
};
测试文件:
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');
const { expect } = chai;
chai.use(chaiHttp);
describe('with correct secret key', () => {
it('should return bracket', (done) => {
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
app = require('../server/server.js');
chai.request(app)
.get('/code-championship/registrant/event')
.end((err, response) => {
expect(response).to.have.status(200);
authenticationMiddleware.rejectUnauthenticated.restore();
done();
});
});
});
我已经尝试过以下类似问题: and this: node express es6 sinon stubbing middleware not working 但我仍然从应该跳过的中间件中获取 403。我还在调试模式下 运行 测试,所以我知道应该存根的中间件函数仍然是 运行ning.
这是存根我的代码的问题吗?这是 ES6 问题吗?
我可以重构我的代码或测试来使它工作吗?
存根代码确实存在问题。
当您需要服务器文件时
const app = require('../server/server.js');
您的应用程序是使用整套中间件创建的,包括 rejectUnauthenticated
,并且对后者的引用存储在 app
.
中
当你这样做时
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
您替换了 authenticationMiddleware
模块的 rejectUnauthenticated
导出方法,但不是对已存储的原始 rejectUnauthenticated
的引用。
解决方案是创建应用程序(即 require('../server/server.js');
)在 模拟导出的中间件方法之后:
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
// don't create app right away
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');
const { expect } = chai;
chai.use(chaiHttp);
describe('with correct secret key', () => {
it('should return bracket', (done) => {
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
// method is stubbed, you can create app now
app = require('../server/server.js');
chai.request(app)
.get('/code-championship/registrant/event')
.end((err, response) => {
expect(response).to.have.status(200);
authenticationMiddleware.rejectUnauthenticated.restore();
done();
});
});
});
根据@Sergey 的建议,我确实切换到了 Jest。至少对于这个特定的案例,它大大简化了实现。对于那些感兴趣的人,这是最终结果:
const express = require('express');
const request = require('supertest');
const registrantRouter = require('../server/routers/registrant.router');
jest.mock('../server/modules/event-authentication.middleware');
const { rejectUnauthenticated } = require('../server/modules/event-authentication.middleware');
const initRegistrantRouter = () => {
const app = express();
app.use(registrantRouter);
return app;
};
describe('GET /registrant', () => {
test('It should 200 if event authentication passes', async (done) => {
const app = initRegistrantRouter();
rejectUnauthenticated.mockImplementation((req, res, next) => next());
const res = await request(app).get('/event');
expect(res).toHaveProperty('status', 200);
done();
});
test('It should 403 if event authentication fails', async (done) => {
const app = initRegistrantRouter();
rejectUnauthenticated.mockImplementation((req, res) => res.sendStatus(403));
const res = await request(app).get('/event');
expect(res).toHaveProperty('status', 403);
done();
});
});
还要感谢这个有用的博客 post 关于使用 Jest 测试快速应用程序:https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest/
我正在尝试测试特定路线的行为。即使我创建存根,它也会继续 运行 中间件。我希望事件认证暂时通过。我知道目前这不是真正的 "unit" 测试。我快到那里了。我还稍微简化了代码。这是要测试的代码:
const { rejectUnauthenticated } = require('../modules/event-authentication.middleware');
router.get('/event', rejectUnauthenticated, (req, res) => {
res.sendStatus(200);
});
这是我试图跳过的中间件:
const rejectUnauthenticated = async (req, res, next) => {
const { secretKey } = req.query;
if (secretKey) {
next();
} else {
res.status(403).send('Forbidden. Must include Secret Key for Event.');
}
};
module.exports = {
rejectUnauthenticated,
};
测试文件:
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');
const { expect } = chai;
chai.use(chaiHttp);
describe('with correct secret key', () => {
it('should return bracket', (done) => {
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
app = require('../server/server.js');
chai.request(app)
.get('/code-championship/registrant/event')
.end((err, response) => {
expect(response).to.have.status(200);
authenticationMiddleware.rejectUnauthenticated.restore();
done();
});
});
});
我已经尝试过以下类似问题:
这是存根我的代码的问题吗?这是 ES6 问题吗?
我可以重构我的代码或测试来使它工作吗?
存根代码确实存在问题。
当您需要服务器文件时
const app = require('../server/server.js');
您的应用程序是使用整套中间件创建的,包括 rejectUnauthenticated
,并且对后者的引用存储在 app
.
当你这样做时
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
您替换了 authenticationMiddleware
模块的 rejectUnauthenticated
导出方法,但不是对已存储的原始 rejectUnauthenticated
的引用。
解决方案是创建应用程序(即 require('../server/server.js');
)在 模拟导出的中间件方法之后:
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
// don't create app right away
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');
const { expect } = chai;
chai.use(chaiHttp);
describe('with correct secret key', () => {
it('should return bracket', (done) => {
sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
.callsFake(async (req, res, next) => next());
// method is stubbed, you can create app now
app = require('../server/server.js');
chai.request(app)
.get('/code-championship/registrant/event')
.end((err, response) => {
expect(response).to.have.status(200);
authenticationMiddleware.rejectUnauthenticated.restore();
done();
});
});
});
根据@Sergey 的建议,我确实切换到了 Jest。至少对于这个特定的案例,它大大简化了实现。对于那些感兴趣的人,这是最终结果:
const express = require('express');
const request = require('supertest');
const registrantRouter = require('../server/routers/registrant.router');
jest.mock('../server/modules/event-authentication.middleware');
const { rejectUnauthenticated } = require('../server/modules/event-authentication.middleware');
const initRegistrantRouter = () => {
const app = express();
app.use(registrantRouter);
return app;
};
describe('GET /registrant', () => {
test('It should 200 if event authentication passes', async (done) => {
const app = initRegistrantRouter();
rejectUnauthenticated.mockImplementation((req, res, next) => next());
const res = await request(app).get('/event');
expect(res).toHaveProperty('status', 200);
done();
});
test('It should 403 if event authentication fails', async (done) => {
const app = initRegistrantRouter();
rejectUnauthenticated.mockImplementation((req, res) => res.sendStatus(403));
const res = await request(app).get('/event');
expect(res).toHaveProperty('status', 403);
done();
});
});
还要感谢这个有用的博客 post 关于使用 Jest 测试快速应用程序:https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest/