I have tests using supertest to connect to a server and I am trying to mock an authentication and an authorisation middlewares with Jest. I have found couple other questions which answers I have tried to combine this question is about mocking an imported function and then there are couple questions about mocking an express middleware: this, this one and also this
I have tried to combine the answers in those questions and came up with this
// server.ts
import express from 'express';
import { authentication } from 'auth-lib'
const server = express();
server.use(express.json());
server.use(authentication);
server.use('/someRoute', someRouter);
export { server };
// someRoute.test.ts
import { Server } from 'http';
import request from 'supertest';
import { server } from '../path/to/server'
describe('Some tests', () => {
let app: Server;
beforeAll(done => {
app = server.listen(3210, done)
});
afterAll(done => {
app.close(() => done())
});
it('POST someRoute', async () => {
/**
* Mocking the authentication middleware
*/
jest.mock('auth-lib', () => ({
__esModule: true,
authentication: (req, res, next): void => {
res.locals.authentication = { auth: true, authToken: 'asdasasd' };
next();
}
}));
/**
* But it doesn't work
*/
const res = await request(app)
.post('/someRoute')
.send(testData);
expect(res.status).toEqual(200);
});
});
This doesn't seem to work. I have had some logging in the mocked middleware and the execution doesn't seem ever even go in there. I have also had some other variants of the mock, but these don't work either
jest.mock('auth-lib', () => ({
__esModule: true,
authentication: jest.fn((req, res, next): void => {
res.locals.authentication = { auth: true, authToken: 'asdasasd' };
next();
})
}));
jest.mock('auth-lib', () => ({
__esModule: true,
authentication: jest.fn().mockImplementation((req, res, next): void => {
res.locals.authentication = { auth: true, authToken: 'asdasasd' };
next();
})
}));
What is the correct way to mock the middlewares so that the res object is also extended and passed forward in the middleware chain?
jest.mock()
only works in module scope. And you don't need pass the factory function, let it
Mocks a module with an auto-mocked version when it is being required. factory and options are optional
Then, you can provide difference mock implementation in each test case.
auth-lib.ts
:
export const authentication = (req, res, next) => {
console.log('real implementation');
};
server.ts
:
import express from 'express';
import { authentication } from './auth-lib';
const server = express();
server.use(express.json());
server.use(authentication);
server.post('/someRoute', (req, res) => {
console.log('res.locals.authentication: ', res.locals.authentication);
res.sendStatus(200);
});
export { server };
someRoute.test.ts
:
import request from 'supertest';
import { server } from './server';
import { authentication } from './auth-lib';
jest.mock('./auth-lib');
const authenticationmock = jest.mocked(authentication);
describe('Some tests', () => {
it('POST someRoute', async () => {
authenticationmock.mockImplementation((req, res, next) => {
res.locals.authentication = { auth: true, authToken: 'asdasasd' };
next();
});
const res = await request(server).post('/someRoute');
expect(res.status).toEqual(200);
});
});
Test result:
console.log
res.locals.authentication: { auth: true, authToken: 'asdasasd' }
at log (stackoverflow/78639299/server.ts:9:11)
PASS stackoverflow/78639299/someRoute.test.ts (8.62 s)
Some tests
√ POST someRoute (38 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 19.408 s
Ran all test suites related to changed files.
package versions:
"express": "^4.19.2",
"jest": "^29.7.0",
"supertest": "^7.0.0",