Express router's mock post handler doesn't work as expected

Viewed 54

I have a documents router which has router.post('/mine', [auth, uploadFile], async (req, res) => { ... }) route handler. The actual implementation of this route handler is below.

documents.js router

const createError = require('./../helpers/createError');
const auth = require('./../middlewares/auth');
const uploadFile = require('./../middlewares/uploadFile');
const express = require('express');
const router = express.Router();

router.post('/mine', [auth, uploadFile], async (req, res) => {
    try {
        let user = await User.findById(req.user._id);

        let leftDiskSpace = await user.leftDiskSpace();

        if(leftDiskSpace < 0) {
            await accessAndRemoveFile(req.file.path);
            res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
        } else {
            let document = new Document({
                filename: req.file.filename,
                path: `/uploads/${req.user.username}/${req.file.filename}`,
                size: req.file.size
            });
            document = await document.save();

            user.documents.push(document._id);
            user = await user.save();

            res.send(document);
        }
    } catch(ex) {
        res.status(500).send(createError(ex.message, 500));
    }
});

module.exports = router;

I'm currently writing integration tests using Jest and Supertest. My current documents.test.js test file is below:

documents.test.js test file

const request = require('supertest');
const { Document } = require('../../../models/document');
const { User } = require('../../../models/user');
const fs = require('fs');
const path = require('path');

let server;

describe('/api/documents', () => {
    beforeEach(() => { server = require('../../../bin/www'); });
    afterEach(async () => {
        let pathToTestFolder = path.join(process.cwd(), config.get('diskStorage.destination'), 'user');
        
        // Remove test uploads folder for next tests
        await fs.promises.access(pathToTestFolder)
            .then(() => fs.promises.rm(pathToTestFolder, { recursive: true }))
            .catch((err) => { return; });

        // Remove all users and documents written in test database
        await User.deleteMany({});
        await Document.deleteMany({});

        server.close();
    });

    describe('POST /mine', () => {
        it('should call user.leftDiskSpace method once', async () => {
            let user = new User({
                username: 'user',
                password: '1234'
            });
            user = await user.save();

            let token = user.generateAuthToken();

            let file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.json');

            let documentsRouter = require('../../../routes/documents');

            let errorToThrow = new Error('An error occured...');

            user.leftDiskSpace = jest.fn().mockRejectedValue(errorToThrow);
            
            let mockReq = { user: user };
            let mockRes = {};

            documentsRouter.post = jest.fn();
            documentsRouter.post.mockImplementation((path, callback) => {
                if(path === '/mine') {
                    console.warn('called');
                    callback(mockReq, mockRes);
                }
            });

            const res = await request(server)
                .post('/api/documents/mine')
                .set('x-auth-token', token)
                .attach('document', file);

            expect(documentsRouter.post).toHaveBeenCalled();
            expect(user.leftDiskSpace).toHaveBeenCalled();
        });
    });
});

I create mock post router handler for documents.js router. As you can see from mockImplementation for this route handler, it checks if the path is equal to '/mine' (which is my supertest endpoint), then calls console.warn('called'); and callback. When I run this test file, I can not see any yellow warning message with body 'called'. And also when POST request endpoint /api/documents/mine the server doesn't trigger my mock function documentsRouter.post. It has never been called. So I think the server's documents router is not getting replaced with my mock post route handler. It still uses original post route handler to respond my POST request. What should I do to test if my mock documentsRouter.post function have been called?

Note that my User model has a custom method for checking left disk space of user. I also tried to mock that mongoose custom method but It also doesn't work.

0 Answers
Related