A guide on using sinon to stub express middleware in a typescript project

I'm currently facing a challenge in writing an integration test for my express router using typescript, mocha, sinon, and chai-http. The router incorporates a custom middleware I created to validate JWT tokens present in the header.

My goal is to mock the authMiddleware so that I can manipulate its behavior without needing to provide valid or invalid JWT tokens for each test case.

However, when attempting to stub the authMiddleware in my tests, I discovered that the express app was utilizing the actual implementation of the middleware instead of the mocked version.

I even tried importing the app after mocking the authMiddleware with dynamic imports in typescript, but unfortunately, it didn't yield the desired outcome either.

authMiddleware.ts

import { Request, Response, NextFunction } from 'express';

export default class AuthMiddleware {
    
    verifyToken(req: Request, res: Response, next: NextFunction) :void {
        console.log('Actual implemetation of verifyToken is called!');
        
        // verify token
        
        next();
    }
}

subjectRouter.ts

import express from'express';
import AuthMiddleware from '../middleware/authMiddleware';
import * as subjectController from '../controller/subjectController';

const router = express.Router();
const authMiddleware = new AuthMiddleware();

router.post('/', authMiddleware.verifyToken, subjectController.createSubject);

export default router;

app.ts

import express from 'express';
import subjectRoute from './route/subjectRoute';

// Initilize express app
const app = express();

app.set("port", 3000);

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// Routers
app.use('/user', userRoute);
app.use('/subject', subjectRoute);

export default app;

subjectTests.ts

import app from '../../src/app';
import AuthMiddleware from '../../../src/middleware/AuthMiddleware';

describe('Subject', () => {

    let app;
    
    beforeEach(async () => {
        sinon.stub(AuthMiddleware.prototype, 'verifyToken').callsFake((req: Request, res: Response, next: NextFunction): void => {
            console.log('Fake verifyToken is called!');
             
            // THIS IS NEVER CALLED IN TESTS...

        });
        app = (await import('../../../src/app')).default;
    });

    it('should throw 403 when jwt is missing in header', (done) => {
        request(app)
            .post(/subject)
            .end((err, res) => {
                expect(res).has.status(403);
                done();
            });
    });
});

Upon running the above test, I noticed that the mocked authMiddleware wasn't being invoked. Instead, the app in the tests continued to leverage the original implementation of the authMiddleware object.

Is there a method to effectively stub express middleware and explicitly pass it to the app?

Answer №1

I have just explained the situation in my response, which can be found here, providing a workaround solution.

Upon further consideration, I believe the most effective way to address this issue is by eliminating global state from your modules and encapsulating the entire initialization code within explicitly invoked functions (or classes if preferred), allowing you to create a new server for each test. Essentially, restructuring your architecture as follows:

// router.ts
export function createRouter() {
    /// ...
    router.post('/', authMiddleware.verifyToken, subjectController.createSubject);
    return router;
}

// app.ts
import { createRouter} from "./router.js"
export function createApp() {
   ///same code as currently, but in function
   app.use('/subject', createRouter());
}

By adopting this approach, you are able to instantiate a new "app" in every setup callback:

// test.ts
beforeEach(async () => {
    sinon.stub(AuthMiddleware.prototype, 'verifyToken').callsFake((req: Request, res: Response, next: NextFunction): void => {
        ...
    });
    app = (await import('../../../src/app')).createApp(); // note that we create new app for each test, preventing pollution of global app state
});

This strategy offers numerous benefits including:

  • The ability to mock various functions for different tests;
  • The elimination of "implicit singletons" from your code, which was the root cause of the initial problem.

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

Tips for handling user click events in Angular 2?

In Angular2, I am facing an issue with two components. When a user clicks a button in component1, a method is triggered that stores data in the shared service to a variable. However, component2's ngOnInit() method initializes this variable to undefine ...

Enforcing Type Safety on String Enums in Typescript Using the 'const' Assertion

I am trying to use an enum for type checking purposes. Here is the enum I have: enum Options { Option1 = "xyz", Option2 = "abc" } My goal is to create a union type of 'xyz' | 'abc'. However, when I attempt to d ...

Leverage Prisma's auto-generated types as the input type for functions

Exploring the capabilities of Prisma ORM has led me to experiment with creating models and generating the PrismaClient. Initially, I thought it would be possible to utilize the generated types for variables and response types, but that doesn't seem to ...

How can we dynamically update property values in a ngFor loop by utilizing the click method?

Here is a TypeScript file I am working with: <md-card style="display: inline-block;" *ngFor="let people of peoples"> <p> {{people.name}} </p> <p *ngIf="people.showAge"> {{people.age}} </p> < ...

Issues are arising with the for loop in an express node js app using ejs, as it is not displaying the intended data and

I am currently utilizing a for loop in JavaScript to display all the users from the database using ejs. I have included the code snippet below. This is within an express/node js application where SQL is used for data storage. <div class = "Contacts ...

What is the best way to import a reusable component from the theme folder in a React Native project?

I'm interested in importing a Button component that can be reused from the theme folder. The path to the Button component is as follows: \app\theme\components\Button.ts Here is the code for Button.ts: import { typography } from ...

Using iOS to send a request - Receiving a response with a 304 HTTP status code from

I am currently in the process of developing an application specifically designed for iPad users. To create this app, I am utilizing Express and a restful approach as the backend framework. The code snippet below demonstrates how Express should respond on t ...

Dealing with a windows-1250 URI within a node.js/express framework

My application relies on a web service to generate its URIs, which sometimes results in a (potentially) windows-1250 encoded string (/punk%92d). Unfortunately, Express encounters an error: Connect 400 Error: Failed to decode param 'punk%92d' ...

Mongoose encounters issues when trying to update a document using findOne() method, as it only

I have a set of fixtures assigned to a specific competitor, displayed in the following format: { "_id": { "$oid": "59dbdf6dbe628df3a80419bc" }, "timeOfEntrance": "1507581805813", "timeOfFinish": null, "competitor": { "$ ...

Unable to declare a string enum in TypeScript because string is not compatible

enum Animal { animal1 = 'animal1', animal2 = 'animal2', animal3 = 'animal3', animal4 = 'animal4', animal5 = 'animal5' } const species: Animal = 'animal' + num Why does typescr ...

Strange occurrences observed in the functionality of Angular Material Version 16

Encountered a strange bug recently. Whenever the page height exceeds the viewport due to mat-form-fields, I'm facing an issue where some elements, particularly those from Angular Material, fail to load. Here's a GIF demonstrating the problem: GI ...

What is the best way to incorporate a style attribute into my EJS page content?

Having trouble with adding custom style. It seems to work, but there's an error displayed. Apologies for my poor english :( <div class="card card_applicant"> <div class="card_title" style="background-c ...

Improve your code quality with TypeScript's type checking capabilities

I am currently utilizing TypeScript version 1.4.1 and I have a need to import an external module (specifically "chai") while ensuring type checking compatibility. Yet, I seem to be facing a naming conflict issue with the following code snippet: /// <r ...

Use Express 4.x to automatically redirect HTTP traffic to HTTPS

Here is the code snippet that I am working with: var https = require('https'); var http = require('http'); var express = require('express'); var app = express(); var router = express.Router() ...

Tips for sending a database query from a Model to a Controller in Express.js

Within my Model file product.js, I have the following code: const mssql = require('mssql/msnodesqlv8'); module.exports.rsProducts = function () { global.MYDB.connect(function (error) { if (error) { console.log(error); return; } global.MYDB.re ...

Webpack bundling only a singular Typescript file rather than all of its dependencies

I'm currently facing a challenge while attempting to consolidate all the files in my Typescript project, along with their dependencies from node_modules, into a single file using Webpack. Despite trying multiple options, it seems that only the entry f ...

Troubleshooting TypeScript: Issues with Object.assign and inheritance

After successfully using the code within an Angular project, I decided to switch to React only to find that the code is now producing unexpected results. class A { constructor(...parts: Partial<A>[]) { Object.assign(this, ...parts); } } cla ...

Unsure about the differences between production and development phases?

Imagine a scenario where a React app interacts with an Express.js server. During development, the React app listens on localhost:3000 and the Express server on localhost:80. In the server-side code, we set the "Allowed origin" to be localhost:3000. The Re ...

Observables do not provide any results when used in a pipe with an image src

I recently created a custom pipe for the image src in my application: It is applied to selectors like this: <img [src]="myobject?.URL | secure" /> Here's the code snippet for the pipe: import { Pipe, PipeTransform } from '@angular/c ...

Issues with loading Angular 9 application on Internet Explorer 11

Having trouble with my app not loading in IE 11 after adding ngx-treeview. Encountering the error (SCRIPT1002: Syntax error), Script Error Error point in vendor.js Unsure how to resolve this issue. Works fine in chrome and firefox, but in IE11 all I se ...