Search code examples
node.jsexpresstestingmocha.jssinon

How to assert that Express app.use is called with middleware function? (Mocha)


I need to test if app.use is called with middleware. app.use is called with functions that returns functions, and I'm not able to test it.

  • app-factory.js
const express = require('express')
const app = express()
const csurf = require('csurf')
const cookieParser = require('cookie-parser')


class AppFactory {
  createApp () {
    app.use(csurf({ cookie: true }))
    app.use(cookieParser())
    return app
  }
}

module.exports = {
  AppFactory: AppFactory,
  app: app,
  csurf: csurf
}
  • app-factory.test.js
const chai = require('chai')
const assert = chai.assert
const rewire = require('rewire')
const appFactoryRewire = rewire('../server/app-factory.js')
const sinon = require('sinon')
const appFactory = require('../server/app-factory')

describe('csurf middleware', () => {
  const rewiredCsurf = appFactoryRewire.__get__('csurf')

  it('should exist', () => {
    assert.exists(rewiredCsurf)
  })

  it('should be a function', () => {
    assert.isFunction(rewiredCsurf)
  })

  it('should be equal csurf module', () => {
    assert.strictEqual(rewiredCsurf, require('csurf'))
  })

  it('should be called once as app.use arg', () => {
    const csurf = appFactory.csurf
    const appUseSpy = sinon.spy(appFactory.app, 'use')
    const appInstance = new appFactory.AppFactory()
    appInstance.createApp()
    sinon.assert.calledWith(appUseSpy, csurf({ cookie: true }))
  })
})

  • The failing test output:
  1) csurf middleware
       should be called once as app.use arg:
     AssertError: expected use to be called with arguments 
Call 1:
function cookieParser() {} function csrf() {} 
Call 2:
function csrf() {}
      at Object.fail (node_modules/sinon/lib/sinon/assert.js:107:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:66:16)
      at Object.assert.<computed> [as calledWith] (node_modules/sinon/lib/sinon/assert.js:92:13)
      at Context.<anonymous> (test/app.test.js:45:18)
      at processImmediate (internal/timers.js:461:21)

if I call the app.use with a string like app.use('/passport', routes) and do sinon.assert.calledWith(appUseSpy, '/passport) it works.

How can I assert that app.use is called with csrf({ cookie: true })?


Solution

  • Can create a helper function to compare function args as strings:

    • app-factory.test.js
    const chai = require('chai')
    const assert = chai.assert
    const rewire = require('rewire')
    const appFactoryRewire = rewire('../server/app-factory.js')
    const sinon = require('sinon')
    
    /**
     * Helper: Returns the argument call number with matching args
     * If none found, returns undefined
     * @param {*} spyFn sinon.Spy function
     * @param {*} argFn callback / function param
     */
    function assertCalledWithFunctionAsArg (spyFn, argFn) {
      const calls = spyFn.getCalls()
      const argFnString = argFn.toString()
      let foundMatch = false
      for (const call in calls) {
        const arg = spyFn.getCall(call).args[0]
        if (arg.toString() === argFnString) {
          // foundCall = spyFn.getCall(call)
          foundMatch = true
        }
      }
      assert(foundMatch === true, 
        'Spy function/method was not called with expected function')
    }
    
    describe('csurf middleware', () => {
      const rewiredCsurf = appFactoryRewire.__get__('csurf')
    
      it('should exist', () => {
        assert.exists(rewiredCsurf)
      })
    
      it('should be a function', () => {
        assert.isFunction(rewiredCsurf)
      })
    
      it('should be equal csurf module', () => {
        assert.strictEqual(rewiredCsurf, require('csurf'))
      })
    
      it('should be called once as app.use arg', () => {
        const csurf = require('csurf')
        const app = appFactoryRewire.__get__('app')
        const AppFactory = appFactoryRewire.__get__('AppFactory')
        const appUseSpy = sinon.spy(app, 'use')
        const appInstance = new AppFactory()
    
        appInstance.createApp()
    
        assertCalledWithFunctionAsArg(appUseSpy, csurf({ cookies: true }))
        sinon.restore()
      })
    })