简体   繁体   中英

Unit Testing Controllers use Jest, NodeJS

I want to check a case that certain routes are calling the correct controller use Jest specific (mock or spy) .

It is case specific for unit testing. Somebody can help me how to check it use jest. I don't need verify kind of expect (status code or res object) i need to check if controller have been called. Thanks!

For instance:

// todoController.js
function todoController (req, res) {
    res.send('Hello i am todo controller')
} 

// index.spec.js
const express = require('express');
const request = require('request-promise');
const todoController = require('./todoController');
jest.mock('./todoController');

const app = express();

app.get('/todo', todoController)

test('If certain routes are calling the correct controller , controller should to have been called times one.', async() => {
    await request({url: 'http://127.0.0.1/todo'})
    expect(todoController).toHaveBeenCalledTimes(1);
})

Actually if you search, there are many references out there.

In the following, I share a few ways that I know.

One of the big conceptual leaps to testing Express applications with mocked request/response is understanding how to mock a chained

API eg. res.status(200).json({ foo: 'bar' }) .

First you can make some kind of interceptor, this is achieved by returning the res instance from each of its methods:

// util/interceptor.js
module.exports = {
  mockRequest: () => {
    const req = {}
    req.body = jest.fn().mockReturnValue(req)
    req.params = jest.fn().mockReturnValue(req)
    return req
  },

  mockResponse: () => {
    const res = {}
    res.send = jest.fn().mockReturnValue(res)
    res.status = jest.fn().mockReturnValue(res)
    res.json = jest.fn().mockReturnValue(res)
    return res
  },
  // mockNext: () => jest.fn()
}

The Express user-land API is based around middleware. AN middleware that takes a request (usually called req), a response (usually called res ) and a next (call next middleware) as parameters.

And then you have controller like this :

// todoController.js
function todoController (req, res) {
    if (!req.params.id) {
      return res.status(404).json({ message: 'Not Found' });
    }

    res.send('Hello i am todo controller')
}

They are consumed by being “mounted” on an Express application (app) instance (in app.js):

// app.js
const express = require('express');
const app = express();

const todoController = require('./todoController');

app.get('/todo', todoController);

Using the mockRequest and mockResponse we've defined before, then we'll asume that res.send() is called with the right payload ({ data }) .

So on your test file :

// todo.spec.js
const { mockRequest, mockResponse } = require('util/interceptor')
const controller = require('todoController.js')

describe("Check method \'todoController\' ", () => {
  test('should 200 and return correct value', async () => {
    let req = mockRequest();
    req.params.id = 1;
    const res = mockResponse();

    await controller.todoController(req, res);

    expect(res.send).toHaveBeenCalledTimes(1)
    expect(res.send.mock.calls.length).toBe(1);
    expect(res.send).toHaveBeenCalledWith('Hello i am todo controller');
  });

  test('should 404 and return correct value', async () => {
    let req = mockRequest();
    req.params.id = null;
    const res = mockResponse();

    await controller.todoController(req, res);

    expect(res.status).toHaveBeenCalledWith(404);
    expect(res.json).toHaveBeenCalledWith({ message: 'Not Found' });
  });
});

This is only 1 approach to testing Express handlers and middleware. The alternative is to fire up the Express server.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM