I am having difficulty getting the expected result while mocking a method within a class using jest and supertest and I was wondering if anyone knew a solution.
I am trying to bypass the verifyAuthenticated method in the original class with the mocked version because it calls out to an external service.
This is a simple example
import express, { Application, Request, Response } from "express";
import authMiddleware from "./AuthMiddleware";
const app: Application = express();
app.disable("x-powered-by");
const rootHandler = (request: Request, response: Response) => {
response.json({ status: "online" });
};
app.get("/", [authMiddleware.verifyAuthenticated, rootHandler]);
export default app;
import { Request, Response, NextFunction } from "express";
class AuthMiddleware {
constructor() {
console.log("AuthMiddleware instance created");
}
verifyAuthenticated = (request: Request, response: Response, next: NextFunction) => {
// In the real world this would call out to an external service and verify user credentials.
console.log("This is the real deal");
next();
};
}
export default new AuthMiddleware();
import request from "supertest";
import app from "../src/app";
import authMiddleware from "../src/AuthMiddleware";
import { NextFunction, Request, Response } from "express";
describe("GET /", () => {
test("Should return online.", async () => {
jest
.spyOn(authMiddleware, "verifyAuthenticated")
.mockImplementation((req: Request, res: Response, next: NextFunction) => {
console.log("This is the mock deal");
next();
});
const agent = request(app);
const response = await agent.get("/").send();
expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toContain("application/json");
expect(response.body.status).toBe("online");
});
});
The test results are as follows:
> node-express-typescript@1.0.0 test
> jest
console.log
AuthMiddleware instance created
at new AuthMiddleware (src/AuthMiddleware.ts:5:13)
console.log
This is the real deal
at AuthMiddleware.verifyAuthenticated (src/AuthMiddleware.ts:9:13)
PASS tests/app.test.ts
GET /
✓ Should return online. (39 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.316 s, estimated 3 s
Ran all test suites.
However I was expecting This is the mock deal
. I have tried multiple different variants on importing and rewriting the class but none of them work and they all give the same result.
Mock the authMiddleware.verifyAuthenticated()
method before importing the app
. We can use dynamic import()
method to import the app
.
app.test.ts
:
import request from 'supertest';
// import app from './app';
import authMiddleware from './AuthMiddleware';
import { NextFunction, Request, Response } from 'express';
describe('GET /', () => {
test('Should return online.', async () => {
jest
.spyOn(authMiddleware, 'verifyAuthenticated')
.mockImplementation((req: Request, res: Response, next: NextFunction) => {
console.log('This is the mock deal');
next();
});
const app = (await import('./app')).default;
const agent = request(app);
const response = await agent.get('/').send();
expect(response.statusCode).toBe(200);
expect(response.headers['content-type']).toContain('application/json');
expect(response.body.status).toBe('online');
});
});
Test result:
console.log
AuthMiddleware instance created
at new AuthMiddleware (stackoverflow/77281063/AuthMiddleware.ts:5:11)
console.log
This is the mock deal
at stackoverflow/77281063/app.test.ts:11:13
PASS stackoverflow/77281063/app.test.ts
GET /
✓ Should return online. (91 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.747 s, estimated 7 s