Search code examples
javascriptnode.jsexpressunit-testinges6-modules

Unit testing Express controller


Been working on this for days now. Finally breaking over and asking for help.

When I have the server running and make API calls, things work as expected.

Now I am trying to unit test the controller and running into issues. Hoping for some clarification as I am obviously missing something vital.

classCodesController.js (base minimum code. I have commented out everything but this in my testing without joy)

import { classCodeModel } from '../models/classCodeModel.js';
import { asyncHandler } from '../utils/asyncHandler.js';

const getAll = async (req, res, next) => {
    const record_set = [];

    res.status(400).json({
        status: 'success',
        length: record_set.length,
        data: record_set
    });
};

export {
    getAll
};

classCodesController.test.js

import assert from 'node:assert/strict';
import { describe, it, mock } from 'node:test';
import mockHttp from 'node-mocks-http';
import { getAll } from '../../src/controllers/classCodeControllerv1.js';
import { classCodeModel } from '../../src/models/classCodeModel.js';

describe('getAll: return JSON', () => {
        it('should return array of two records as JSON', { timeout: 5000 }, async () => {
            const req = mockHttp.createRequest();
            const res = mockHttp.createResponse();
            const next = mock.fn();

            await getAll(req, res, next);

            assert.deepStrictEqual(res.statusCode, 400);
            assert.deepStrictEqual(res.json(body), { status: 'success', length: 0, data: [] });
    });
});

When called, the status code does not change and the json is not returned.

I have tried passing my own res object which worked when I unit tested middleware modules.

            const res = {
                body: '',
                data: {},
                message: undefined,
                statusCode: 100, // noramlly defaults to 200
                statusText: '', // normally 'OK'
                headers: {},
                config: {},
                reqeust: {},
                send: mock.fn(),
                status: mock.fn(c => {
                    this.statusCode = c;
                    return this;
                }),
                json: mock.fn(d => {
                    this.message = d;
                    console.log('json d: ', d);
                })
            }

The console.log in json function outputs the controller submitted data, but message is still undefined (TypeError: Cannot set properties of undefined (setting 'message')) when it gets to the test and the statusCode has not changed.

All help is appreciated. Thanks in advance.


Solution

  • From the documentation, you should use res._getJSONData() to get the response JSON data. Below is a working example:

    getAll.js:

    const getAll = async (req, res) => {
        const record_set = [];
        res.status(400).json({
            status: 'success',
            length: record_set.length,
            data: record_set,
        });
    };
    
    export { getAll };
    

    getAll.test.js:

    import assert from 'node:assert/strict';
    import { describe, it, mock } from 'node:test';
    import mockHttp from 'node-mocks-http';
    import { getAll } from './getAll.js';
    
    describe('getAll: return JSON', () => {
        it('should return array of two records as JSON', async () => {
            const req = mockHttp.createRequest();
            const res = mockHttp.createResponse();
            const next = mock.fn();
    
            await getAll(req, res, next);
    
            assert.deepStrictEqual(res.statusCode, 400);
            assert.deepStrictEqual(res._getJSONData(), { status: 'success', length: 0, data: [] });
        });
    });
    

    Test result:

    $ node --test ./getAll.test.js
    ▶ getAll: return JSON
      ✔ should return array of two records as JSON (1.215ms)
    ▶ getAll: return JSON (2.3065ms)
    
    ℹ tests 1
    ℹ suites 1
    ℹ pass 1
    ℹ fail 0
    ℹ cancelled 0
    ℹ skipped 0
    ℹ todo 0
    ℹ duration_ms 80.1445
    

    package.json:

    {
      "name": "77646756",
      "version": "1.0.0",
      "type": "module",
      "devDependencies": {
        "node-mocks-http": "^1.14.0"
      }
    }
    

    Node version:

    $ nvm ls
    
      * 21.4.0 (Currently using 64-bit executable)
        18.19.0
        16.20.2