Javascript 如何在模块中存根函数?
我创建express应用程序时,有一条路线可以使用许多中间件:Javascript 如何在模块中存根函数?,javascript,testing,mocking,mocha.js,sinon,Javascript,Testing,Mocking,Mocha.js,Sinon,我创建express应用程序时,有一条路线可以使用许多中间件: // fblogin.js const saveUser = require('./middlewares').saveUser; const issueJWT = require('./middlewares').issueJWT; const exchangeLongTimeToken = (a) => { //return promise to call API }; const retrieveUserInfo =
// fblogin.js
const saveUser = require('./middlewares').saveUser;
const issueJWT = require('./middlewares').issueJWT;
const exchangeLongTimeToken = (a) => { //return promise to call API };
const retrieveUserInfo = (b) => { //return promise to call API };
const service = {
exchangeLongTimeToken,
retrieveUserInfo,
};
const asyncAll = (req, res) => {
// use Promise.all() to get service.exchangeLongTimeToken
// and service.retrieveUserInfo
};
router.post('/', [asyncAll, saveUser, issueJWT], (req, res) => {
//some logic;
});
module.exports = { router, service };
这是我的middleware.js
:
const saveUser = (req, res, next) => { //save user };
const issueJWT = (req, res, next) => { //issue jwt };
module.exports = { saveUser, issueJWT };
它工作得很好。但是当我试着写测试时遇到了问题。
这是我的测试,我使用摩卡、柴、supertest和sinon:
const sinon = require('sinon');
const middlewares = require('../../../../src/routes/api/auth/shared/middlewares');
const testData = require('../../testdata/data.json');
let app = require('../../../../src/config/expressapp').setupApp();
const request = require('supertest');
let service = require('../../../../src/routes/api/auth/facebook/fblogin').service;
describe('I want to test', () => {
context('Let me test', function () {
const testReq = {name: 'verySad'};
beforeEach(() => {
sinon.stub(middlewares, 'saveUser').callsFake((req, res, next)=>{
console.log(req);
});
sinon.stub(service, 'exchangeLongTimeToken').callsFake((url) => {
return Promise.resolve(testData.fbLongTimeToken);
});
sinon.stub(service, 'retrieveUserInfo').callsFake((url) => {
return Promise.resolve(testData.fbUserInfo);
});
});
it('Should return 400 when bad signedRequest', () => {
return request(app).post(facebookAPI).send(testReq).then((response) => {
response.status.should.be.equal(400);
});
});
});
问题出在哪里您可以看到有3个存根,1个用于
middleware.saveUser
,2个用于services.XXXX
,它们位于路由的同一文件中
问题是,2个存根有效,而中间件.saveUser
的1个存根无效,总是触发原始存根
我想,当我调用setupApp()
时,express可能会加载它所需的所有路由器,因此以后模拟它不会有效果,但它
奇怪的是,route.service
可以被模仿
如何获得存根工作?让它工作的唯一方法是将存根放在测试文件的顶部,就在
中间件要求之后
我试过:
1.使用第三方模块,如proxyquire
,rewire
2.使用节点自己的delete require.cache[middleware]
和“app”并重新请求它们。
3.还有许多其他技巧。
4.使用jest的mock,但仅当我将其放在文件顶部时,它仍然有效
在不将存根放在测试文件顶部的情况下,解决此问题的方法是什么?谢谢 问题的解决方案有点受限,因为模拟已经污染了整个测试套件
最后,我这样做了,逻辑很简单,我们仍然需要先模拟saveUser
,但是我们需要将所有其他变量放入测试函数中,而不是将它们放在文件的顶部,这一次更加灵活。我添加了一个checkIfTheStubWorks
方法来检查存根是否工作,以确保整个测试工作正常
const middlewares = require('../../../../src/routes/api/auth/shared/middlewares');
const removeEmptyProperty = require('../../../../src/utils/utils').removeEmptyProperty;
let app;
let service;
let request;
/*
* The reason we need this is:
* If the mock not works,
* the whole test is meaningless
*/
const checkIfTheStubWorks = () => {
expect(spy1).toHaveBeenCalled();
expect(spy2).toHaveBeenCalled();
expect(spy3).toHaveBeenCalled();
};
const loadAllModules = () => {
service = require('../../../../src/routes/api/auth/facebook/fblogin').service;
app = require('../../../../src/config/expressapp').setupApp();
request = require('supertest')(app);
};
describe('Mock response from facebook', () => {
let spy1 = {};
let spy2 = {};
let spy3 = {};
const testReq = testData.fbShortToken;
beforeAll(() => {
spy1 = jest.spyOn(middlewares, 'saveUser').mockImplementation((req, res, next) => {
userToSaveOrUpdate = removeEmptyProperty(res.locals.user);
next();
});
// It must be load here, in this order,
// otherwise, the above mock won't work!
loadAllModules();
spy2 = jest.spyOn(service, 'exchangeLongTimeToken').mockImplementation((url) => {
// mock it
});
spy3 = jest.spyOn(service, 'retrieveUserInfo').mockImplementation((url) => {
// mock it
});
});
afterAll(() => {
spy1.mockRestore();
spy2.mockRestore();
spy3.mockRestore();
});
test('Return a JWT should have same length as facebook one', async () => {
const response = await request.post(facebookAPI).send(testReq);
// assert this, assert that
checkIfTheStubWorks();
});
});