Ensuring that Express app.use is triggered with a middleware function: A guide using Mocha

I am faced with the challenge of testing whether app.use is being called with middleware. The issue arises when app.use is called with functions that return other functions, making it difficult to conduct proper testing.

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

When I attempted to simulate calling app.use with a string using something like app.use('/passport', routes) and then validated it with

sinon.assert.calledWith(appUseSpy, '/passport')
, it worked successfully. However, my struggle lies in asserting that app.use is correctly called with csrf({ cookie: true }). Any suggestions on how I can properly achieve this assertion?

Answer №1

Implementing a helper function to compare function arguments 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')

/**
 * Custom Helper Function: Check if argument is called with expected function
 * @param {*} spyFn - Sinon Spy function
 * @param {*} argFn - Callback or function parameter
 */
function verifyFunctionArgCall (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) {
      foundMatch = true
    }
  }
  assert(foundMatch === true, 
    'Spy function/method was not called with the expected function')
}

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

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

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

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

  it('should be called once with app.use argument', () => {
    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()

    verifyFunctionArgCall(appUseSpy, csurf({ cookies: true }))
    sinon.restore()
  })
})

Answer №2

Software testing solution:

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('Testing csurf middleware functionality', () => {
  it('Verifies that csurf middleware is called once as an argument by app.use', (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();
    });
  });
});

Test Results:

  Testing csurf middleware functionality
    ✓ Verifies that csurf middleware is called once as an argument by app.use (214ms)


  1 passing test case (223ms)

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Monitor the download status of clients from the server

My current situation involves numerous Linux-based clients that are downloading firmware updates from my webserver. Once the client has completed the download of the firmware file, my server is required to run a series of scripts. These scripts will log i ...

Having trouble updating a specific document in my MongoDB collection

I am facing an issue with my function that reads data from the database. I am trying to change the value of the "hasVoted" field in my Voters model from false to true, but it is not reflecting in the database. Even after setting data.hasVoted = true and c ...

Transmitting files instantly without the need for a response body

Currently, Express is serving index.html file by default even though I haven't specified the response body yet. This is how my file structure looks: node_modules public img abc.html index.html script.js ...

Authentication is necessary for the "insert" command when establishing a connection to a remote database server | error code: 13, error code name

I have successfully established a connection to the remote server const express =require("express"); const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const Promise = require(&apo ...

Dynamic content is not displaying when utilizing the email-template-v2 package in Node.js

When using email-template-v2 in nodejs to send dynamic data to a template, only the variable is displayed instead of the dynamic content. Here is the code snippet: var template = new EmailTemplate(templateDir) var nodemailer = require('nodemailer&ap ...

There is no compatible version available for @typescript-eslint/[email protected]

I am encountering the following issue when trying to create a new app or running npm install. I need assistance in resolving this error. I have also attempted using the command below but the error persists. npm install -g create-react-ap Installing packa ...

Automatically update and reload Express.js routes without the need to manually restart the server

I experimented with using express-livereload, but I found that it only reloaded view files. Do you think I should explore other tools, or is there a way to configure express-livereload to monitor my index.js file which hosts the server? I've come ac ...

"Master chef preparing a delicious dish with fresh ingredients, skillfully incorporating node, npm

As I set up node and npm using the 'nodejs' cookbook and included the recipe through include_recipe "nodejs::npm" I proceeded to install the desired packages with the following code: %w( dateformat aws2js optparse cloudwatch2graphite).each do ...

Is it possible to run a React, Vite, Node.js, and Express app all at once

I am currently learning React and JavaScript, but I am facing challenges when it comes to connecting my frontend and backend. I have been attempting to run both the front and back end simultaneously in order to send requests to the backend and display fetc ...

error message: sendFile function not recognizing basename and dirname parameters

My current dilemma involves sending a file located at '/x/y/file.pdf' to be opened in the browser. I am utilizing nodejs Express as my server-side platform, and for file transmission, I am using the sendFile() method. By leveraging the path.relat ...

Is it feasible to indent lines in a template without affecting the content alignment?

Creating a string with newlines that will be included in an email later. if (action) { description = ` Git pull request action: ${action} Git pull request for repo: ${req.body.repository.full_name} Git pull request for repo URL: ${re ...

Can you provide the function that updates subscription and account details using Recurly JS?

Recurly has unfortunately declined to provide assistance with this issue. I must understand the specific format of the objects, as Recurly will not process any extra data or incorrect query arguments (which vary for each function). Ruby: subscription = ...

What regular expression should be used to meet the following requirement in JavaScript?

Criteria: Find words that begin with 'a' and end with 'b', with a digit in the middle, but are not on lines starting with '#' Given string: a1b a2b a3b #a4b a5b a6b a7b a8b a9b Expected output: a1b a2b a3b a7b a8b ...

Having trouble installing my package with npm

I've been encountering an issue while attempting to set up a package that I have specified the package.json for. The contents of the package.json file are quite straightforward. { "name": "project", "version": "0.0.1", "devDependencies": { ...

Encountering a Typescript error while attempting to utilize mongoose functions

An example of a User interface is shown below: import {Document} from "mongoose"; export interface IUser extends Document{ email: string; password: string; strategy: string; userId: string; isValidPassword(password: string): ...

Guide to Setting User Permissions with CheckboxList in MERN Stack

I have been working on implementing user permissions in MERN using a checkbox list. Initially, I tried manually granting access by using an if and else statement to give fixed authorization based on the user's role. Here's how it looks: {user.ro ...

Developing single-page application frontends without utilizing node or npm

As a backend java developer with experience in spring boot, I am now exploring the world of single page applications. One aspect that appeals to me about SPA frameworks, particularly Vue, is things like model-binding and the use of components and template ...

Is there a way to allow an HTML page rendered by node.js to communicate back to its corresponding node.js file?

I am currently in the process of developing a registry system using node.js and HTML. However, I have encountered an issue where my HTML page is rendered by node.js, but when trying to call it back to the node.js file, it appears that the JS file cannot be ...

How to add multiple entries using Node.js and Tedious

I am currently working with an array of customer objects that I need to insert into a SQL database. These customer objects are retrieved from the request data. For this task, I am utilizing Tedious for handling the request and Tedious Connectionpool to ma ...

How can I utilize angular's $http service to fetch a JavaScript file?

I've been exploring the integration of Angular with Node.js and attempting to establish a connection to a MySQL database. Within my script (server.js), I am utilizing the node mysql module as shown below: var mysql=require('mysql'); var ...