I have a file with two routes.
routes/index.js
const express = require('express')
const router = express.Router()
router.get('', (req, res, next) => {
try {
res.status(200).render('../views/')
} catch (error) {
next(error)
}
})
router.get('*', (req, res, next) => {
try {
res.status(404).render('../views/not-found')
} catch (error) {
next(error)
}
})
module.exports = router
I want to test the catch branches. One of the obvious problems is that I can't fake what the callback of router.get does, because then my test would be pointless, I would modify the very thing that I want to test.
I can send a Supertest request to these specific routes, but then I have no control over what happens. I want to avoid creating a hardcoded route, only so I can check whether the middleware is called by the next function. If I understand correctly, if I would have additional things happening in the try block (like a database query) than I could mock that to throw an error.
But I do not have any additional things happening there. Here is what I can not wrap my head around: if I mock what the server does, then I don't test what I already have but something else, so there is no point in doing that. Maybe I misunderstand how things work, but as far as I can see the only option would be to somehow mock what res.status or res.render does (stub them so they throw an error), so somehow mock what Supertest does, but I have no idea how to do that.
Any help would be greatly appreciated!
unit test solution:
route.js
:
const express = require('express');
const router = express.Router();
router.get('', (req, res, next) => {
try {
res.status(200).render('../views/');
} catch (error) {
next(error);
}
});
router.get('*', (req, res, next) => {
try {
res.status(404).render('../views/not-found');
} catch (error) {
next(error);
}
});
module.exports = router;
route.test.js
:
describe('64051580', () => {
afterEach(() => {
jest.resetModules();
jest.restoreAllMocks();
});
it('should render views', () => {
const express = require('express');
const mRouter = { get: jest.fn() };
jest.spyOn(express, 'Router').mockImplementationOnce(() => mRouter);
const mReq = {};
const mRes = { status: jest.fn().mockReturnThis(), render: jest.fn() };
const mNext = jest.fn();
mRouter.get.mockImplementation((path, callback) => {
if (path === '') {
callback(mReq, mRes, mNext);
}
});
require('./route');
expect(mRes.status).toBeCalledWith(200);
expect(mRes.render).toBeCalledWith('../views/');
});
it('should handle error', () => {
const express = require('express');
const mRouter = { get: jest.fn() };
jest.spyOn(express, 'Router').mockImplementationOnce(() => mRouter);
const mReq = {};
const mErr = new Error('parse');
const mRes = {
status: jest.fn().mockReturnThis(),
render: jest.fn().mockImplementationOnce(() => {
throw mErr;
}),
};
const mNext = jest.fn();
mRouter.get.mockImplementation((path, callback) => {
if (path === '') {
callback(mReq, mRes, mNext);
}
});
require('./route');
expect(mNext).toBeCalledWith(mErr);
});
it('should render 404 not found view', () => {
const express = require('express');
const mRouter = { get: jest.fn() };
jest.spyOn(express, 'Router').mockImplementationOnce(() => mRouter);
const mReq = {};
const mRes = { status: jest.fn().mockReturnThis(), render: jest.fn() };
const mNext = jest.fn();
mRouter.get.mockImplementation((path, callback) => {
if (path === '*') {
callback(mReq, mRes, mNext);
}
});
require('./route');
expect(mRes.status).toBeCalledWith(404);
expect(mRes.render).toBeCalledWith('../views/not-found');
});
});
unit test result with coverage report:
PASS src/stackoverflow/64051580/route.test.js
64051580
✓ should render views (656ms)
✓ should handle error (17ms)
✓ should render 404 not found view (16ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 90.91 | 100 | 100 | 90.91 | |
route.js | 90.91 | 100 | 100 | 90.91 | 16 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 4.28s, estimated 10s