So, today we are going to see how do we mock the middleware if it is applied over the Express server.
So, here is our agenda:
is-user-authentic.js
middleware.js
welcome-route.js
welcome-service.js
In this code, the flow is like:
In this test there are certain things that needs to be taken care before loading the express server.
So, here is our agenda:
- Create an express app
- Add a middleware to Authenticate the user
- Test the app using Jest (integration test NOT UNIT)
index.js
server.js
1 2 3 | const server = require('./server') const PORT = process.env.PORT || 3000 server.listen(PORT, () => console.log(`Server is live at localhost:${PORT}`)) |
server.js
1 2 3 4 5 6 7 | const express = require('express') const middleware = require('./middleware') const welcomeRoute = require('./welcome-route') const server = express() server.use(express.json()) server.use('/api', middleware, welcomeRoute) module.exports = server |
is-user-authentic.js
1 2 3 4 5 | const faker = require('faker/locale/en_US') const isUserAuthentic = () => faker.random.arrayElement([true, false]) module.exports = { isUserAuthentic } |
middleware.js
1 2 3 4 5 6 7 8 9 10 | const { isUserAuthentic } = require('./is-user-authentic') const middleware = (req, res, next) => { if (!isUserAuthentic()) { res.sendStatus(401) return next(new Error('Unauthenticated user')) } next() } const myExport = module.exports = middleware |
welcome-route.js
1 2 3 4 5 | const { Router } = require('express') const WelcomeService = require('./welcome-service') const router = Router() router.get('/', (_req, res) => res.json({ 'message': WelcomeService.message() })) module.exports = router |
welcome-service.js
1 2 3 | module.exports = { message: () => 'hello world' } |
In this code, the flow is like:
- Start the server at 3000 (line 3, index.js)
- Middleware is applied to base path (line 6, server.js)
- If you hit http://localhost:3000/api, is-user-authentic will return true/false randomly which will return either return 'hello world' or 401 unauthorised
Now in order to test this, we will use jest mocking.
routes.test.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | const request = require('supertest') const faker = require('faker/locale/en_US') let app let WelcomeServiceSpyOfMessage let IsUserAuthenticSpyOnIsUserAuthentic describe('test', () => { beforeEach(() => { /** * Always create spy then create server */ const WelcomeService = require('../src/welcome-service') WelcomeServiceSpyOfMessage = jest.spyOn( WelcomeService, 'message', ) /** * Always create spy then create server */ const IsUserAuthentic = require('../src/is-user-authentic') IsUserAuthenticSpyOnIsUserAuthentic = jest.spyOn( IsUserAuthentic, 'isUserAuthentic' ) app = require('../src/server') }) afterEach(() => { /** * Most important since b'coz of caching, the mocked implementations sometimes does not resets */ jest.resetModules() jest.restoreAllMocks() }) it('1. Mock implementation, with successful user auth [isUserAuthentic as true]', async () => { const mockedMessage = faker.lorem.sentence() WelcomeServiceSpyOfMessage.mockImplementation(() => mockedMessage) // --> For successful user authentication, sending true : IsUserAuthenticSpyOnIsUserAuthentic.mockImplementation(() => true) const result = await request(app) .get('/api') expect(result.statusCode).toBe(200) expect(result.body).toHaveProperty('message', mockedMessage) }) it('2. After restored implementation, with successful user auth [isUserAuthentic as true]', async () => { /** * This will return { 'message' : 'hello world'}, * since we restored the mock implementation, in afterEach * restoreAllMocks and resetModules, * works only for methods those are mocked * with .spyOn() */ // --> For successful user authentication, sending back true IsUserAuthenticSpyOnIsUserAuthentic.mockImplementation(() => true) const result = await request(app) .get('/api') expect(result.statusCode).toBe(200) /** * This expectation is the same returning from the service */ expect(result.body).toHaveProperty('message', 'hello world') }) it('3. Returning user is Unauthorized, [isUserAuthentic as false]', async () => { // for Unauthorized, we return false IsUserAuthenticSpyOnIsUserAuthentic.mockImplementation(() => false) const result = await request(app) .get('/api') expect(result.statusCode).toBe(401) }) }) |
In this test there are certain things that needs to be taken care before loading the express server.
- Mock the middleware before you load the server otherwise if you mock middleware after the server is loaded new reference of the object will be mocked and even after the mock the original implementation will be called.
- In this test case, we loaded the server at line 28, but the spy was already hooked to the functions before that only.
- In afterEach we will reset modules and reset mocks to make sure mocked implementation of one test will not interfere in other tests.
- Now every time module is loaded then spy is added and mock will be hooked.
The source code for the above example is available at https://github.com/ankur20us/demo-jest-supertest
Happy Coding
:)
No comments:
Post a Comment