I have an express app like this:
server.js
const postsController = require('./controllers/posts_controller.js')
module.exports = app = express()
app.get('posts', postsController.index)
posts_controller.js
const post = require('./post')()
module.exports = {
index: (req, res) => {
post.getAll().then((posts) => {
res.status(200).send(posts)
}, (error) => {
res.status(400).send('text')
})
}
}
post.js
module.exports = () => {
const thirdPartyApi = require('third_party_api')
return {
get: () => {
// do some stuff
return thirdPartyApi.get().then((resp) => {
// do some more stuff
return Promise.resolve(resp)
})
}
}
}
spec/posts_controller_spec.js
const app = require('../server')
const request = require('supertest')
describe('GET /posts', () => {
it('should return a collection of posts', () => {
request(app)
.get('/posts')
.end((_err, resp) => {
expect(resp.status).toEqual(200)
})
})
})
My goal here is to stub out the thirdPartyApi.get()
. I tried with proxyquire
by adding this line to posts_controller_spec:
proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
This doesn't work because the server.js file is the file that requires the third_party_api again.
I could do something like this to test the controller:
const postsController = proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
postsController.index(req, res)
This second strategy doesn't feel right because now I have to stub req
and res
and now I'm bypassing the actual app
instance.
Is there an easy way to do this, with proxyquire, or otherwise?
I realized what's going on, proxyquire is not actually messing up here.
the file post.js
exports a function, so when posts_controller.js
requires() the post.js file, it executes the function and the require for third_party_api
is evaluated again and the stub is wiped out.
This is the "runtimeGlobal" scenario described here: https://www.npmjs.com/package/proxyquire#globally-override-require-during-module-runtime
Solution is to fix up post.js
so that it doesn't export a function:
const thirdPartyApi = require('third_party_api')
return {
get: () => {
// do some stuff
return thirdPartyApi.get().then((resp) => {
// do some more stuff
return Promise.resolve(resp)
})
}
}
Now, as long as this happens before the app is initialized, the
proxyquire('../posts_controller', {third_party_api: {
get: () => { console.log('stubbed out get method'); }
})
Then third_party_api module that gets required inside the post controller is evaluated at load time and it gets cached as expected.