Ray Saudlach
Ray Saudlach

Reputation: 650

Jest - Express Test

I have a controller for simple car buying/selling API which does this

async processCarSale(@Res() res: any) {
    if(carSaleService.sale()) {
        return res.status(201).send(<some-body>);
    }
    return res.status(204).send(<some-other-body>);
}

I am trying to test that the status code is set correct with a unit test. Googling and using SO I found example similar to this

const mockResponse = {
    status: jest.fn(),
    send: jest.fn()
}
const mockResponseSent = { send: jest.fn() };
mockResponse.status = jest.fn(() => mockResponseSent);

await carController.processCarSale(response);
expect(response.statusCode).toBe(200);

However this does not seem to actually test the status code.

Is there any way to test the statusCode sent in the response via Express?

Upvotes: 0

Views: 3171

Answers (2)

A Mehmeto
A Mehmeto

Reputation: 1999

You can achieve this by using supertest (provided by default with Nestjs).

You can use the also provided HttpStatus enum which provides all HTTP code status.

test('/route (POST)', () => {
  const data = { any: 'data', what: 'you want' }
  request(app.getHttpServer())
            .post('/route')
            .send(data)
            .expect(HttpStatus.CREATED)
            .expect('Content-Type', /json/)
}

Upvotes: 1

Jay McDoniel
Jay McDoniel

Reputation: 70061

Your mock doesn't ever set a value called statusCode, it just stubs the functionality and returns the same object so that send() can be called as well (also a stub). If you want to be able to test something like res.statusCode === 200 you could do something like this:

const mockResponse = {
    status: jest.fn(),
    send: jest.fn(),
    statusCode: 0
}
const mockResponseSent = { send: jest.fn() };
mockResponse.status = jest.fn(() => {
  mockResponseSent.statusCode = 200;
  return mockResponseSent;
});

await carController.processCarSale(response);
expect(response.statusCode).toBe(200);

However, this is essentially jsut re-writing the logic of your controller in the first place, which isn't really good. So what would be better would be to take advanatage of Jest's mockImplementation() method where you can do

const mockResponse = {
    status: jest.fn(),
    send: jest.fn()
}
const mockResponseSent = { send: jest.fn() };
mockResponse.status = jest.fn().mockImplementation((status) => {
  mockResponseSent.statusCode = status;
  return mockResponseSent;
});

await carController.processCarSale(response);
expect(response.statusCode).toBe(200);

Overall, though, I would suggest ditching the use of res in your controller and suggest letting Nest handle the setting of the http status code and sending the response. You can set the code to something other than default by using @HttpCode()

Upvotes: 2

Related Questions