I try to set stub fakes for a express middleware function and it's not replacing over.
I'm trying to use sinon stubbing via callsFake function, just as it's advised from their most updated docs.
Even though I'm requiring the module and replacing the function from the property at the export. I keep seeing the original function behavior acting.
I know that I should try to get the function stubbed before the middleware functions get setup, and that's when express app is first imported.
This is the function I'm trying to stub, defined as a function and exported as a object too. It's defined in a script file with a path like api/middlewares/stripe/signature.
const stripeHelper = require('../../../lib/stripe')
const logger = require('../../../lib/logger')
const verifySignature = (req, res, next) => {
var event
let eventName = req.url.replace('/', '')
try {
// Try adding the Event as `request.event`
event = stripeHelper.signatureCheck(
eventName,
req.body,
req.headers['stripe-signature']
)
} catch (e) {
// If `constructEvent` throws an error, respond with the message and return.
logger.error('Error while verifying webhook request signature', e.message, e)
return res.status(400).send('Webhook Error:' + e.message)
}
req.event = event
next()
}
module.exports.verifySignature = verifySignature
beforEach hooks in order to organize my stubs and preconditions or testThis is my stubs and test hooks setup:
const chai = require('chai')
const chaiHttp = require('chai-http')
const dirtyChai = require('dirty-chai')
const sinon = require('sinon')
const decache = require('decache')
const signatureMiddleware = require('../../../api/middlewares/stripe/signature')
const bp = require('body-parser')
let verifySignatureStub, rawStub
chai.should()
chai.use(dirtyChai)
chai.use(chaiHttp)
const API_BASE = '/api/subscriptions'
const planId = 'NYA-RUST-MONTHLY'
const utils = require('../../utils')
const {
hooks: {createSubscription, emitPaymentSucceeded},
stripe: {generateEventFromMock}
} = utils
let testUser, testToken, testSubscription, server
describe.only('Subscriptions renewal (invoice.payment_succeeded)', function () {
this.timeout(30000)
beforeEach(function (done) {
createSubscription(server, {planId}, function (err, resp) {
if (err) return done(err)
const {user, jwt, subscription} = resp
console.log(user, jwt)
testUser = user
testToken = jwt
testSubscription = subscription
done()
})
})
beforeEach(function (done) {
verifySignatureStub = sinon.stub(signatureMiddleware, 'verifySignature')
rawStub = sinon.stub(bp, 'raw')
rawStub.callsFake(function (req, res, next) {
console.log('bp raw')
return next()
})
verifySignatureStub.callsFake(function (req, res, next) {
const {customerId} = testUser.stripe
const subscriptionId = testSubscription.id
console.log('fake verify')
req.event = generateEventFromMock('invoice.payment_failed', {subscriptionId, customerId, planId})
return next()
})
done()
})
beforeEach(function (done) {
decache('../../../index')
server = require('../../../index')
const {customerId} = testUser.stripe
const {id: subscriptionId} = testSubscription
console.log(`emitting payment succeeded with ${customerId}, ${subscriptionId} ${planId}`)
emitPaymentSucceeded(server, testToken, function (err, response) {
if (err) return done(err)
done()
})
})
afterEach(function (done) {
verifySignatureStub.restore()
done()
})
it('Date subscription will renew gets set to a valid number roughly one month', function () {
// Not even getting here becasue calling the original function contains verifyMiddleware which should be replaced
})
it('Current period end is modified')
it('An invoice for the new starting period is generated')
it('Subscription status keeps active')
})
Context (please complete the following information):
All runs over Node 8 and I'm running tests with mocha and did a set up with dirty chai.
These are my dev dependencies:
"devDependencies": {
"base64url": "^2.0.0",
"cross-env": "^5.0.5",
"decache": "^4.4.0",
"dirty-chai": "^2.0.1",
"faker": "^4.1.0",
"google-auth-library": "^0.12.0",
"googleapis": "^23.0.0",
"minimist": "^1.2.0",
"mocha": "^5.2.0",
"nodemon": "^1.12.0",
"nyc": "^11.2.1",
"sinon": "^6.1.5",
"standard": "^10.0.3",
"stripe-local": "^0.1.1"
}
Try to create a mock of the desired modules with mock-require NPM's module:
https://www.npmjs.com/package/mock-require
By this way, whenever your code does a "require" it will actually import the "mocked" one (which by definition is a stub).
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I'd be amazed if this was actually an issue of Sinon and not some detail of the module loading, with which Sinon has nothing to do. The setup is way too complicated to be meaningful in pointing to any bug, as well. I'd try rewire, proxyquire and the likes.
We are trying to keep the GitHub issues list tidy and focused on bugs and feature discussions. This ticket looks like a usage question; please post it to StackOverflow and tag it with sinon, so the bigger community can help answer your questions.
If you feel that your topic is an issue with Sinon, please open a new ticket and follow the guidelines for reporting an issue.
To other people finding this issue, I believe I spotted the reason why sinon was not working when I encountered this same issue (stubs not working at all) and came up with the following steps when using sinon for testing an express server + chai:
decache('../../path/to/express/index.js');functionStub = sinon.stub(someImport, 'someFunction');app = require(''../../path/to/express/index.js');After step 2, step 3 will pull up the stubs instead of the original functions.
What I believe happened in the OP's code snippet is that the order of 1 and 2 were switched, which means that he was creating the stubs, and then decache was clearing them.
This is definitely not an issue with sinon but more of module loading as @fatso83 mentioned.
The resulting code would look something like:
beforeEach(async () => {
// Step #1, clear cached imports
decache('../../../index');
// Step #2, create the stubs
verifySignatureStub = sinon.stub(signatureMiddleware, 'verifySignature')
rawStub = sinon.stub(bp, 'raw')
rawStub.callsFake(function (req, res, next) {
console.log('bp raw')
return next()
})
verifySignatureStub.callsFake(function (req, res, next) {
const {customerId} = testUser.stripe
const subscriptionId = testSubscription.id
console.log('fake verify')
req.event = generateEventFromMock('invoice.payment_failed', {subscriptionId, customerId, planId})
return next()
})
// Step #3, import server
server = require('../../../index');
// Now use server with the stubs
createSubscription(server, {planId}, function (err, resp) {
if (err) return done(err)
const {user, jwt, subscription} = resp
console.log(user, jwt)
testUser = user
testToken = jwt
testSubscription = subscription
done()
})
const {customerId} = testUser.stripe
const {id: subscriptionId} = testSubscription
console.log(`emitting payment succeeded with ${customerId}, ${subscriptionId} ${planId}`)
emitPaymentSucceeded(server, testToken, function (err, response) {
if (err) return done(err)
done()
})
// Let the tests run using the stubs.
})
Hope this helps someone!
Most helpful comment
To other people finding this issue, I believe I spotted the reason why sinon was not working when I encountered this same issue (stubs not working at all) and came up with the following steps when using sinon for testing an express server + chai:
decache('../../path/to/express/index.js');functionStub = sinon.stub(someImport, 'someFunction');app = require(''../../path/to/express/index.js');After step 2, step 3 will pull up the stubs instead of the original functions.
What I believe happened in the OP's code snippet is that the order of 1 and 2 were switched, which means that he was creating the stubs, and then
decachewas clearing them.This is definitely not an issue with sinon but more of module loading as @fatso83 mentioned.
The resulting code would look something like:
Hope this helps someone!