RawBody not working for testingModule.createNestApplication(...) in nest.js - nestjs

I have trouble with rawbody webhook requests in my testinstance. In my normal instance which I setup with the following it works fine:
const app = await NestFactory.create(AppModule, {
logger: logger,
rawBody: true
});
In system test I setup my test instance like this
const testingModule: TestingModule = await Test.createTestingModule({
imports: [AppModule],
})
.compile();
const app = testingModule.createNestApplication({ rawBody: true });
and in my controller:
public async webhook(#Req() req: RawBodyRequest, ...
the req.rawBody is undefined and thus validation fails. i checked and rawBody is indeed a valiud parameter of createNestApplication. So I wonder what I may be missing...
Or could this have sth. to do with supertest I use for testing (https://github.com/ladjs/supertest/issues/675 suggests that may be in issue in supertest but also indicates usage of express middleware settings may prevent it). Does anybody know how I can test properly if this is a nest.js or supertest issue?
Thanks
Tom

Related

SpyOn express route handler function in controller

So i have a simple express route which I am trying to test using Jest. Below are the code snippets
auth.route.ts
import * as authController from '../controllers/auth.controller';
const router = express.Router();
// POST /api/user/register
router.post(`${userApis.register}`, authController.register);
export { router };
auth.route.test.ts
it('register', async() => {
const spy = jest.spyOn(authController, "register");
await request(app).post('/api/user/register');
expect(spy).toHaveBeenCalled();
});
But the test fails. Below is the error.
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
One way i got it working is re writing my route as below but that is not the right way i think
import * as authController from '../controllers/auth.controller';
const router = express.Router();
// POST /api/user/register
router.post(`${userApis.register}`, (req, res, next) => {
authController.register(req, res, next);
});
export { router };
What would be solution for this. I am new to Jest.
Thanks in Advance
* import object is read-only by specs and can fail to be spied or mocked depending on a specific setup.
router.post(`${userApis.register}`, authController.register)
line is evaluated on import, jest.spyOn in test scope is evaluated later and cannot affect it.
In case an import should be only spied and not mocked, this can be done for all tests in a module mock:
import * as authController from '../controllers/auth.controller';
jest.mock('../controllers/auth.controller', () => {
const controller = jest.requireActual('../controllers/auth.controller');
jest.spyOn(controller, "register");
return controller
});
That register call is asserted doesn't have a lot of value, asserting a response that is expected from this route handler would be enough. A request most likely causes side effects (database queries), they can be mocked or asserted instead.

What is the correct order of requiring and mocking files using Jest?

I'm trying to create an integration test using Jest for my Express app. I think I have a conceptual misunderstanding as my tests are behaving strangely. My goal is to test the following scenario. I'm hitting a specific endpoint using Supertest, and I want to check whether an error handler middleware is called if there is a mocked error. I want to check whether the error handler is not called, if there is no error present. I have the following test file:
test.js
const request = require('supertest')
describe('Error handler', () => {
let server
let router
beforeEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
afterEach(async () => {
await server.close()
})
it('should be triggered if there is a router error', async () => {
jest.mock('../../routes/')
router = require('../../routes/')
router.mockImplementation(() => {
throw new Error()
})
server = require('../../server')
const res = await request(server)
.get('')
.expect(500)
.expect('Content-Type', /json/)
expect(res.body.error).toBe('Error')
expect(res.body.message).toBe('Something went wrong!')
expect(res.body.status).toBe(500 )
})
it('should not be triggered if there is no router error', async () => {
server = require('../../server')
const res = await request(server)
.get('')
.expect(201)
.expect('Content-Type', /text/)
})
})
What I think is happening is the following. Before each test I reset all modules, because I don't want to have the cached version of my server from the first require, I want to overwrite it. I also reset all mocks, so when the second test runs, no mock is used, no fake error is forced, so the middleware is not called and I'm getting back a vanilla 200 result.
After this is done, I start testing the scenario when there is an error. I mock the routes file that exports my routes so I can force a fake error. Then I require the server, this way, I suppose, it's loading the server up with the fake, error throwing route. Then I wait for the response with Supertest, and assert that I indeed got an error back - hence the error handler middleware has been triggered and worked.
The afterEach hook is called, the server is closed, then the beforeEach hook initializes everything, again. Now I have my vanilla implementation without the mock. I require my server, hit the homepage with a get request, and I get back the correct response.
The strange thing is that for some reason the second test seems to not exit gracefully. If I change my implementation from async - await in the second test, to specify the done callback, and then if I call it at the end of the test, it seems to be working.
I tried a lot of possible permutations, including putting the mocking part to the beforeEach hook, starting the server before / after mocking, and I got weird results. I feel like I have conceptual misunderstandings, but I don't know where, because there are so many moving parts.
Any help to make me understand what is wrong would be greatly appreciated
EDIT:
I thought that most parts can be considered a black box, but now I realize that the fact that I'm trying to create an app using Socket.IO makes the setup process a bit more convoluted.
I don't want Express to automatically create a server for me, because I want to use socketIO. So for now I only create a function with the appropiate signature, and that is 'app'. This can be given as an argument to http.Server(). I configure it with options and the middlewares that I want to use. I do not want to call app.listen, because that way Socket.IO could not do its own thing.
config.js
const path = require('path')
const express = require('express')
const indexRouter = require('./routes/')
const errorHandler = require('./middlewares/express/errorHandler')
const app = express()
app.set('views', path.join(__dirname + '/views'))
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.use('', indexRouter)
app.use(errorHandler)
module.exports = app
In server.js I require this app, and then I create a HTTP server using it. After that, I feed it to 'socket.io', so it is connected to the proper instance. In server.js I do not call server.listen, I want to export it to a file that actually starts up the server (index.js) and I want to export it to my tests, so Supertest can spin it up.
server.js
// App is an Express server set up to use specific middlewares
const app = require('./config')
// Create a server instance so it can be used by to SocketIO
const server = require('http').Server(app)
const io = require('socket.io')(server)
const logger = require('./utils/logger')
const Game = require('./service/game')
const game = new Game()
io.on('connection', (socket) => {
logger.info(`There is a new connection! Socket ID: ${socket.id}`)
// If this is the first connection in the game, start the clock
if (!game.clockStarted) {
game.startClock(io)
game.clockStarted = true
}
game.addPlayer(socket)
socket.on('increaseTime', game.increaseTime.bind(game))
})
module.exports = server
If I understand everything correctly, basically the same thing happens, expect for a few additional steps in the example that you provided. There is no need to start the server, and then use Supertest on it, Supertest handles the process of starting up the server when I use request(server).get, etc.
EDIT 2
Right now I'm not sure whether mocking like that is enough. Some mysterious things leaves the Supertest requests hanging, and it might be that somewhere along the way it can not be ended, although I do not see why would that be the case. Anyway, here is the router:
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
The order of requiring and mocking is correct but the order of setting up and shutting down a server probably isn't.
A safe way is to make sure the server is available before doing requests. Since Node http is asynchronous and callback-based, errors cannot be expected to be handled in async functions without promisification. Considering that server.listen(...) was called in server.js, it can be:
...
server = require('../../server')
expect(server.listening).toBe(true);
await new Promise((resolve, reject) => {
server.once('listening', resolve).once('error', reject);
});
const res = await request(server)
...
close is asynchronous and doesn't return a promise so there's nothing to await. Since it's in a dedicated block, a short way is to use done callback:
afterEach(done => {
server.close(done)
})
In case errors are suppressed in error listener, server.on('error', console.error) can make troubleshooting easier.
Supertest can handle server creation itself:
You may pass an http.Server, or a Function to request() - if the server is not already listening for connections then it is bound to an ephemeral port for you so there is no need to keep track of ports.
And can be provided with Express instance instead of Node server, this eliminates the need to handle server instances manually:
await request(app)
...

Jest with Typescript error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout

I'm studying how to create some tests using the Jest with Nodejs, i'm actually using typescript.
When I try to run a simple test, by checking the status of the response, it shows the following error:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
What could I do?
Here's my following codes:
session.test.ts =>
const request = require('supertest');
import app from '../../src/server';
describe('Authentication',() => {
it('should authenticate with valid credentials',async() =>{
const response = await request(app)
.post('/sessions')
.send({
email: "myemail#gmail.com",
password: "123456"
})
await expect(response.status).toBe(200);
});
});
SessionController.ts =>
import {Request, Response} from 'express';
export default class SessionController{
async store(request: Request, response: Response){
return response.status(200);
}
}
server.ts =>
import express from 'express';
import routes from './routes';
require("dotenv").config({
path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
});
const app = express();
app.use(express.json());
app.use(routes);
app.listen(3333);
export default app;
and routes.ts:
import express from 'express';
import UsersController from './controllers/UsersController';
import SessionController from './controllers/SessionController';
const routes = express.Router();
const usersControllers = new UsersController();
const sessionController = new SessionController();
routes.post('/users',usersControllers.create);
routes.post('/sessions',sessionController.store);
export default routes;
at my SessionController.ts, I had to put the following:
import {Request, Response} from 'express';
export default class SessionController{
async store(request: Request, response: Response){
return response.status(200).send()
}
}
I forgot to send haha
The first thing is to check if there are some error in the request, or (more likely) if it remain in a pending state, because 5 seconds are tons of time.
Anyway you can specify the test timeout like follow
describe('Authentication',() => {
it('foobar', async function () { // no arrow function
this.timeout(10000)
await myFunc()
});
});
I am not sure you are actually 'completing' the request using the supertest API.
The fluent chaining approach of supertest allows you to carry on adding 'steps' to the HTTP request before actually dispatching it. Unfortunately, send() is preparing a send step for when you dispatch. It doesn't actually dispatch as you can see from this superagent example in which many further configuration steps follow send() and it's only end() which runs them all.
In several supertest examples I saw there is a chained 'expect' call which would presumably also trigger the actual HTTP post.
Equally, the 'end()' docs at https://github.com/visionmedia/supertest#endfn say...
Perform the request and invoke fn(err, res)
This indicates to me that until you send a 'finalising' call, there won't be a request.

How to test a routing handler calling via mock from express

I'm unit-testing some code of mine in nodejs that works with express.
While it seems obvious, I want to test that my routes are calling the right handler functions.
I'm using jest to create a mock function for the handler function.
While I successfully tried to test that a function is called inside another function, I cannot seem to test that a function (handler) is called from express get of post.
Let's see an examplee:
fruits.js
function orange (req,res,next) {res.status(200)} ;
module.exports = {orange}
app.js
const fruits = require('./fruits')
const express = require('express');
const app = express();
app.get('/fruits', fruits.orange);
module.exports = { app };
When I'm trying to test (with jest) that GET request (with the help of axios module) to /fruits is calling orange(), altough actually orange() works, it won't be saved as a calling with a mock function cover of spy cover.
How can I test that orange() has been called from the GET request ?
Try using supertest with Jest
const app = require('../app') //path to app.js
const request = require('supertest')
test('Upload Info Endpoint', async() => {
await request(app).get('/fruits')
.expect(resp.body.field).toMatch(mocked_field_value)
});

How to startup the server when testing an Express app via Mocha

I would like to write unit tests using Mocha for my Nodejs/Express app that I have written in visual studio. I have scoured everywhere I could looking for a simple tutorial but not found what I am looking for. I have seen many tutorials in creating a test using assert to test that 5=5, etc. but that's not what I want to do.
I am trying to add a JavaScript Mocha Unit Test file through VS and then all I really want it to do is open the home page of my app, check for some content in the body and pass the test. If I want to run the tests from the Test Explorer window the nodejs app can't be running and if it isn't running there would be nothing to receive the request for the homepage.
So I'm not sure if the test itself is somehow supposed to launch the app or what? I feel like I'm in a catch 22 and missing the very basics, just don't see it described anywhere.
What you're looking for is most commonly called an API test - a part of integration testing, not a unit test. If a test touches network, a database or I/O it's, most commonly, an integration test instead.
Now to your question. In order to test your app.js code without starting up the server manually beforehand you can do the following:
module.export your app server.
In your tests, use chai-http to test routes.
require your app in the tests and use that instead of URL's when testing routes.
The key here is the 1st bullet point. You must export your app so you can require it and use it in your tests. This allows you to skip the part where you start a separate server process to run the tests on.
Server code
// app.js
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.json())
// Routes
app.post('/register', (req, res) => {
const requiredFields = ['name', 'email']
if (requiredFields.every(field => Object.keys(req.body).includes(field))) {
// Run business logic and insert user in DB ...
res.sendStatus(204)
} else {
res.sendStatus(400)
}
})
app.listen(3000)
// export your app so you can include it in your tests.
module.exports = app
Test code
// test/registration.spec.js
const chai = require('chai')
const chaiHttp = require('chai-http')
// `require` your exported `app`.
const app = require('../app.js')
chai.should()
chai.use(chaiHttp)
describe('User registration', () => {
it('responds with HTTP 204 if form fields are valid', () => {
return chai.request(app)
.post('/register')
.send({
name: 'John Doe',
email: 'john#doe.com'
})
.then(res => {
res.should.have.status(204)
})
.catch(err => {
throw err
})
})
it('responds with HTTP 400 if some fields are missing', () => {
return chai.request(app)
.post('/register')
.send({
name: 'John Doe'
})
.catch(err => {
err.should.have.status(400)
})
})
})
Then just run your test from the root directory with:
$ mocha test/registration.spec.js

Resources