sinonjs / sinon

Test spies, stubs and mocks for JavaScript.

Home Page:https://sinonjs.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stub called inside Promise then() doesn't have updated .called properties

lozadaOmr opened this issue · comments

  • Sinon version : 15.0.3

  • Environment : NodeJS

  • Other libraries you are using:

    • bluebird
    • mongoose
    • mocha

What did you expect to happen?

  • Should be able to have the following findByIdStub.called , secretsWriteStub.called and next.called be equal to true on the test file

What actually happens

  • Only the initial stub (findByIdStub) has the .called equal to true, while the rest resulted in false
  • I know the stub.resolves work (eg: secretsWriteStub would resolve 'write stub secret')
  • Just to compare when doing this on the function, all .called result in true:
testPromise () {
  let user = this.services.secrets.write({ id: req.user._id })
  next(user)
  res.json(404)
  return 'stop'
}
  • This lead me think that any stubs called after the initial stub.resolve() is not behaving as expected

How to reproduce

  • Only have two relevant files, and just run gulp test
// middleware.js
'use strict'

class Middleware {
  constructor (model, services, utils) {
    this.services = services
    this.model = model
    this.utils = utils
  }

  testPromise () {
    return (req, res, next) => {
      return this.services.secrets.write({ id: req.user._id })
        .then(secret => {
          res.json(203)
          next(false)

          console.log(`within next --- `, next.called)
          console.log(`within res --- `, res.json.called)

          return 'ends here'
        })
    }
  }
}

module.exports = Middleware
// middleware.test.js
'use strict'

global.Promise = require('bluebird')

const sinon = require('sinon')
const mongoose = require('mongoose')

const Secrets = require('path/to/stubbed/class') // secrets.write()
const Middleware = require('path/to/Middleware/class')

describe(`DEMO`, function () {
  describe(`# testPromise ()`, function () {
    let sandbox = null
    let middleware = null

    let model = null
    let utils = null
    let services = null

    let req = null
    let res = null
    let next = null

    let findByIdStub = null
    let secretsWriteStub = null

    before(function () {
      sandbox = sinon.createSandbox()

      req = { user: { _id: '1234890' } }
      res = { json: sandbox.stub().returnsArg(0) }

      let userResult = { userSecret: 'N3Kz+' }

      findByIdStub = sandbox.stub(mongoose.Model, 'findById')
        .usingPromise(global.Promise)
        .withArgs(req.user._id).resolves(userResult)

      model = { findById: findByIdStub }

      secretsWriteStub = sandbox.stub(Secrets.prototype, 'write')
        .usingPromise(global.Promise).resolves('write stub secret')

      services = {
        secrets: { write: secretsWriteStub }
      }
      utils = {}
    })

    afterEach(function () {
      sandbox.restore()
    })

    it(`should run DEMO `, function (done) {
      middleware = new Middlware(model, services, utils)

      next = sandbox.stub().returnsArg(0)
      let middlewareCall = middleware.testPromise()(req, res, next)

      // assertion should go here but use console.log to prevent it from failing while debugging
      console.log(`next stub ====> `, next.called)
      console.log(`resJsonStub =====> `, res.json.called)
      console.log(`secretswrite ====>  `, secretsWriteStub.called)

      done()
    })
  })
})

middlewareCall is a promise

So I did access stubbed next.called, res.json.called, secretsWriteStub.called by adding a then block

middlewareCall
  .then(() => {
    // assertion now goes in here
    console.log(`next stub ====> `, next.called)
    console.log(`resJsonStub =====> `, res.json.called)
    console.log(`secretswrite ====>  `, secretsWriteStub.called)
    
    done()
  })

Only problem with this is that, when assertion fails I receive a an error, instead of telling me which assertion fails