2
votes

Description

I try to set stub fakes for a express middleware function and it's not replacing over.

What I'm trying (how to reproduce)

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

What I tried already

  • Use decache to make sure the express app instance is pristine and it's not being initialized with previous original middleware
  • Set multiple beforEach hooks in order to organize my stubs and preconditions or test

What keeps happening

  • The original middleware function gets executed
  • I don't see any logs of the stub functions (as second proof that sinon stub is not working

This 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" }

Open issue

https://github.com/sinonjs/sinon/issues/1889

1

1 Answers

2
votes

As a rule of thumb, stubs should be set up per test, i.e. in beforeEach or it, not in before. Here they don't seem to contain per-test logic but they can, and in this case they won't work as expected with before. mocha-sinon should preferably be used to integrate Mocha with Sinon sandbox, so no afterEach is needed to restore stubs, this is done automatically.

Since verifySignature is export property and not the export itself, signatureMiddleware module may be left as is, but modules that use it should be de-cached and re-imported in tests where they are expected to use verifySignature. If the behaviour should be same for entire test suite, this should be performed in beforeEach as well. E.g. if these middlewares are used in app module directly, it's:

const decache = require('decache');

...

  describe(() => {
    let app;

    beforeEach(() => {
      verifySignatureStub = sinon.stub(signatureMiddleware, 'verifySignature');
      ...
    });

    beforeEach(() => {
      decache('./app');
      app = require('./app');
    });