如何断言 Express app.use 是用中间件函数调用的? (摩卡)

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

我需要测试 app.use 是否被中间件调用。 app.use 使用 returns 函数调用,我无法对其进行测试。

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
}
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 }))
  })
})

  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)

如果我用 app.use('/passport', routes) 之类的字符串调用 app.use 并执行 sinon.assert.calledWith(appUseSpy, '/passport) 它会起作用。

如何断言 app.use 是用 csrf({ cookie: true }) 调用的?

单元测试解决方案:

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,
};

app-factory.test.js:

const chai = require('chai');
const rewire = require('rewire');
const sinon = require('sinon');
const { expect } = chai;

describe('csurf middleware', () => {
  it('should be called once as app.use arg', (done) => {
    const appFactory = rewire('./app-factory');
    const mCsurfRequestHandler = sinon.stub();
    const mCsurf = sinon.stub().returns(mCsurfRequestHandler);
    const mCookieParserRequestHandler = sinon.stub();
    const mCookieParser = sinon.stub().returns(mCookieParserRequestHandler);
    appFactory.__with__({
      csurf: mCsurf,
      cookieParser: mCookieParser,
    })(() => {
      const appUseStub = sinon.stub(appFactory.app, 'use');
      const appInstance = new appFactory.AppFactory();
      const actual = appInstance.createApp();
      expect(actual).to.be.equal(appFactory.app);
      sinon.assert.calledWithExactly(mCsurf, { cookie: true });
      sinon.assert.calledOnce(mCookieParser);
      sinon.assert.calledWith(appUseStub, mCsurfRequestHandler);
      sinon.assert.calledWith(appUseStub, mCookieParserRequestHandler);
      done();
    });
  });
});

单元测试结果:

  csurf middleware
    ✓ should be called once as app.use arg (214ms)


  1 passing (223ms)

----------------|---------|----------|---------|---------|-------------------
File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------------|---------|----------|---------|---------|-------------------
All files       |     100 |      100 |     100 |     100 |                   
 app-factory.js |     100 |      100 |     100 |     100 |                   
----------------|---------|----------|---------|---------|-------------------

可以创建一个辅助函数来将函数参数作为字符串进行比较:

  • 应用-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()
  })
})