I'm trying to mock the return value of a fastify-auth plugin using Sinon but seems like I am missing something. Here is my implementation:
// routes.js
const fastifyAuth = require('fastify-auth')
const gipAuth = require('./share/fastify/gip-bearer-auth-plugin')
async function restv2 (fastify) {
fastify
.register(gipAuth)
.post('/test', require('./controllers/test').test)
}
module.exports = async function (fastify) {
await fastify.register(fastifyAuth)
return fastify
.register(restv2, { prefix: '/v2' })
}
// gip-bearer-auth-plugin.js
const fp = require('fastify-plugin')
const { verifyGipToken } = require('../helpers/verifyGipToken')
const verifyGipTokenPlugin = async fastify =>
fastify
.addHook('onRequest', fastify.auth([verifyGipToken]))
module.exports = fp(verifyGipTokenPlugin)
// verifyGipToken.js
const AuthorizationError = require('../errors/authorization-error')
async function verifyGipToken (req) {
throw new AuthorizationError('verifyGipToken is not implemented)
}
module.exports = { verifyGipToken }
and here is the part where I try to mock the module in the test file
// test.js
const t = require('tap')
const sinon = require('sinon')
const verifyGipToken = require('/path/to/verifyGipToken')
t.test('test case', async t => {
const sandbox = sinon.createSandbox()
t.beforeEach(async t => {
sandbox.stub(verifyGipToken, 'verifyGipToken').callsFake(async req => {
console.log('verifyGipToken stubb called')
return true
})
})
t.afterEach(async t => {
sandbox.restore()
})
and the part that I'm logging does not show in the console, I get only the result from the original code
I have replicated your issue and code structure here: https://github.com/Eomm/blog-posts/pull/39/files I would like to write a blog post about it.
Anyway the issue is that you are mocking the wrong reference of the
verifyGipTokenfunction.Mocking is not magic in CJS, it just manipulates the
require.cacheobject. Here is a nice home-made mock function: https://github.com/fastify/fastify-cli/issues/453What is happening in your code:
routes.jsfile is required and it loads all the dependenciesverifyGipToken.jsfile is loaded too and stored in therequire.cacheobjectverifyGipTokenfunction modifying therequire.cacheobjectverifyGipTokenfunction that is already loaded in therequire.cacheobject, so it is not the mocked one!Quick fix:
By doing this, the
require()function is called again, so therequire.cacheobject is updated with the new reference of theverifyGipTokenfunction (the mocked one) and the test will pass as expected.Another solution is to avoid the destructuring of the
verifyGipToken.js:This works because
refpoints to a value in therequire.cacheobject, so thestub.mockmodify the value and all its references will be updated too.In summary, mocking is cool, but it is very important to understand how it works under the hood otherwise it can be very frustrating while debugging or writing tests. If your tests fail after a refactoring, it is very likely that you are mocking the wrong reference.
As a suggestion: I would avoid to mock anything that is not an HTTP call or it can be a source of endless side effects.