I have a MERN stack application that I am running a test on using jest and supertest. I am new to testing and was following a tutorial. However, mine keep failing. My MERN stack application has redis database instance for storing users' refresh token. It also uses node crone to carry out scheduled background tasks.
I am getting these errors when I ran my tests:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
And this error:
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
I have used npm test --detectOpenHandles to see if I could detect the asynchronous operations but I didnt see any. Like, it wasn't revealed.
Here is my test:
const request = require('supertest')
const app = require('../app')
describe('Test POST /register', ()=>{
test('It should respond with 200 success', async ()=>{
const response = await request(app).post('/register');
expect(response.statusCode).toBe(200);
},);
test('It should catch password must be present', ()=>{});
})
I have tried adding this line of code to the test:
afterAll(async () => { await app.close(); });
It didn't resolve it. I have also tried adding done(), it didn't work too. I have also increased the timeOut which I added as second argument in test, still the same thing.
I am testing my routes and here is the current one I tried which is register route.
router.post('/register', register);
Here is the app.js file
app.use("/api/v1/auth", authRoute);
app.use("/api/v1/users",userRoute);
app.get('*', (req, res)=>{
res.sendFile(path.join(__dirname, 'build', 'index.html'));
})
app.use(checkDB);
module.exports = app;
Here is the server.js
const http = require('http');
require('dotenv').config();;//declaring the envy file
const app = require('./app');
//mongoDb
const connectDB = require('./db/connect');
const port = process.env.PORT || 5000;
const server = http.createServer(app);
const start = async () => {
try {
await connectDB(process.env.MONGO_URL)
server.listen(port, () =>
console.log(`Server is listening on port ${port}...`)
);
} catch (error) {
console.log(error);
}
};
start();
Related
I am trying learn to use jest and supertest to run my api test. It is my first time and I am following a tutorial. However, my test has refused to work. I am getting these errors:
thrown: "Exceeded timeout of 80000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
And this error at the same time:
st did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
I have tried npm test --detectOpenHandles to see the asyn operations that werent stopped but I didnt see any.
I am making a test of my API routes. Here is my test:
const request = require('supertest')
describe('Test POST /register', ()=>{
test('It should respond with 200 success', async ()=>{
const response = await request(app).post('/register');
expect(response.statusCode).toBe(200);
},80000);
test('It should catch password must be present', ()=>{});
The route that I am testing:
router.post('/register', register);
The app.js file:
const authRoute = require("./routes/auths")
My server.js file:
const server = http.createServer(app);
const start = async () => {
try {
await connectDB(process.env.MONGO_URL)
server.listen(port, () =>
console.log(`Server is listening on port ${port}...`)
);
} catch (error) {
}
};
start();
It is a MERN stack with redis database instance. The redis is used to store users' refresh tokens
How can I fix this?
I read a lot of answers to similar questions already but can't figure out what is wrong in my code.
this is my server.js file
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
app.use(require('koa-bodyparser')())
const login = (ctx, next) => {
ctx.body = ctx.request.body
}
const router = new Router({ prefix: '/api' })
router.get('/test', (ctx, next) => {
ctx.body = { resp: 'GET REQUEST /test WORKING' }
})
router.post('/login', login)
app.use(router.routes())
module.exports = app
this is my index.js file
const server = require('./server')
server.listen(3000, () => {
console.log('App is running on http://localhost:3000')
})
and this is my mocha test file
const axios = require('axios').default
const expect = require('chai').expect
const app = require('./server')
describe('7-module-3-task', () => {
describe('test', function () {
let server
before(done => {
server = app.listen(3000, done)
})
after(async () => {
server.close()
})
it('should return response from server', async () => {
const response = await axios.get('http://localhost:3000/api/test')
expect(response.data, 'should return object with key "resp').to.have.property('resp')
})
})
})
It's working okay when I make a request in Postman. I tried multiple options already but I still get 404 response, as I understand test is performed before server started running...? How can I make it working ?
First, I would move the startup (app.listen) directly into the server.js (not critical, but maybe more simpler because the require in your test would then already start your server:
const Koa = require('koa')
const Router = require('koa-router')
const app = new Koa()
app.use(require('koa-bodyparser')())
const router = new Router({ prefix: '/api' })
router.get('/test', (ctx, next) => {
ctx.body = { resp: 'GET REQUEST /test WORKING' }
})
app.use(router.routes())
app.listen(3000); // can be a parameter but for simplicity hardcoded here
module.exports = app
In your test you then do:
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('./server'); // this will already start up your server
describe('API Tests', () => {
describe('TEST endpoint', () => {
it('It should GET response from test endpoint', (done) => {
chai.request('http://localhost:3000')
.get('/api/test/') // be sure to have a trailing '/' !!
.end((err, res) => {
res.body.should.have.property('resp');
done();
});
})
});
});
One more hint: maybe in your original code you just have to make sure, that you have a trailing / when calling your route in the test.
Code snippets not testet but I hope you get the idea.
I shared the same code with 2 of my friends and they managed to run tests successfully.
I tested it on my other laptop after this and tests worked as well.
The problem was in the port. 3000 port was used as a default one in the debugger in Webstorm, not sure why but still.
Launching the server on port 3000 in a regular way, not in mocha tests, worked very well but in tests, it did not work, not sure why.
So for those who ever face something similar, check the default port of the debugger or any other built-in server.
I make two 'supertest' requests one after each other like so:
const request = require('supertest');
const server = require('#bin/www');
it('should do something', async () => {
// prepare data
const data = { ... some data }
// create business
const res = await request(server)
.post('/v2/businesses')
.send({
...data
});
// store the returned business id
const b_id = res.body.data.id;
// now the critical point - to make another http request to the api,
// in order to fetch the business and expect it to be defined.
const res1 = await request(server)
.get('/v2/businesses')
.query({
b_id
});
// expectations...
expect(res.body.data.business).toBeDefined();
});
The test passes on first time, after pressing "Enter" to test again, I got the
following error:
listen EADDRINUSE: address already in use :::3002
The only solution is to kill the whole test process and start it all over again.
The error also occurs on different test suites as well.
This is the command to run the tests using JEST (in the package.json):
"test": "jest --forceExit --detectOpenHandles --watchAll --maxWorkers=1"
But this doesn't work as expected.
There are the imported files from where the server object comes from:
// bin/www
var app = require('#app/app');
var server = require('#app/server');
server.listen(... some callback)
module.exports = app;
// app/app
var app = require('express')();
module.exports = app;
// app/server
var app = require('#app/app');
var server = require('http').createServer(app);
module.exports = server;
As I understand, the server is probably keep running even after the test finishes, then when firing another test, the server status is already active and in use, so therefore this error.
How can I close the server after each test?
I have tried:
afterEach(() => server.close())
But got an error that server.close is not a function.
Just use conditional listen:
// Turn off listen, when you are testing
if (process.env.NODE_ENV !== "test") {
app.listen(process.env.PORT || 4000);
}
Jest using special NODE_ENV, that called test, so you don't need an listen method. Also supertest is providing unical port for every app instance.
I am running unit test with my node project using Jest library everything was working pretty fine. When I created new test for route authentication it starts showing server is already running on port 4000 even I am using afterEach() function to close the serve but don't know why am still getting server is already running on port 4000.
Even I have removed the new test for route authentication and restart my project by closing all the terminals but whenever I run the project it starts showing error that server is already running on port 4000.
Here is the code in user test file where am closing server properly and on the next test file I am again using same functions for server connection and closing.
const request = require("supertest");
const { Genre } = require("../../models/genre");
const { User } = require("../../models/user");
const mongoose = require("mongoose");
let server;
describe("/api/genres", () => {
beforeEach(() => {
server = require("../../index");
});
afterEach(async () => {
server.close();
await Genre.remove({});
});
second test file code
const { User } = require("../../models/user");
const { Genre } = require("../../models/genre");
const request = require("supertest");
describe("auth middleware", () => {
beforeEach(() => {
server = require("../../index");
});
afterEach(async () => {
await Genre.remove({});
server.close();
});
Here is the output of............................
● auth middleware › should return 401 if no token is provided
listen EADDRINUSE: address already in use :::4000
10 |
11 | const port = process.env.PORT || 4000;
> 12 | const server = app.listen(port, () =>
| ^
at Function.listen (node_modules/express/lib/application.js:618:24)
at Object.<anonymous> (index.js:12:20)
at Object.<anonymous> (tests/integration/auth.test.js:6:14)
Test Suites: 1 failed, 3 passed, 4 total
Tests: 1 failed, 26 passed, 27 total
Snapshots: 0 total
Time: 13.606s
Ran all test suites.
Index.js Code
const winston = require("winston");
const express = require("express");
const app = express();
require("./startup/logging")();
require("./startup/routes")(app);
require("./startup/db")();
require("./startup/config")();
require("./startup/validation")();
const port = process.env.PORT || 4000;
const server = app.listen(port, () =>
winston.info(`Listening on port ${port}...`)
);
module.exports = server;
I am not an expert in Node.js, but the nature of your bug is very simple; you're trying to listen on a port that is already being listened on.
This post offers a solid description of require(), which I believe is the source of your trouble.
By calling require("../../index") in each of your test case programs, you are effectively exporting server twice, which results in two app.listen() calls.
You would be infinitely better off exporting your server a single time to some main test program, in which you could then run all your test cases by calling require() on each of the test files. This approach is much better in the long run as well because adding additional test cases would 1000x easier; you could just write a new test script, export it to your main test program, and append it to your list of test cases.
Your test is creating the server (index.js) multiple times. This makes the server trying to listen to the same port in many instances. The Jest documentation reads,
If you have some work you need to do repeatedly for many tests, you
can use beforeEach and afterEach.
It is obvious that you should create the server one time, do all the tests and close it one time. The methods for this are beforeAll and afterAll.
In some cases, you only need to do setup once, at the beginning of a
file. This can be especially bothersome when the setup is
asynchronous, so you can't do it inline. Jest provides beforeAll and
afterAll to handle this situation.
So, your test should look like,
describe("auth middleware", () => {
beforeAll(() => {
server = require("../../index");
});
afterAll(async () => {
await Genre.remove({});
server.close();
});
I am testing an nodejs-app with a server and a client component on nodejs 8.9 with mocha.
For mocha to end properly, I have to make sure that all socketio and http-servers are closed after the tests have been run. This works fine with normal tests, but as soon as I register a middleware to the socketio-server, the mocha-process won't close and stay open forever.
Testcode (comment in the second test to see the problem, run via mocha test.spec.js):
// test.spec.js
'use strict'
const Express = require('express')
const Http = require('http')
const ioserver = require('socket.io')
const ioclient = require('socket.io-client')
const NODE_PORT = process.env.NODE_PORT || 3000
describe('Client', function () {
beforeEach(() => {
const express = new Express()
this._http = Http.Server(express)
this._ioserver = ioserver(this._http)
this._http.listen(NODE_PORT)
})
// this test works perfectly, even when I copy it and run it
// multiple times in this suite
it('should connect to a socketio-server', (done) => {
this._ioserver.on('connection', () => {
client.close()
done()
})
const client = ioclient.connect(`http://localhost:${NODE_PORT}`)
})
// this test also finished, but the suite hangs afterwards - as if
// a socket-client or socket-server was not closed properly.
it('should finish the test suite even with a middleware', (done) => {
this._ioserver.use((socket, next) => {
return next()
})
this._ioserver.on('connection', () => {
client.close()
done()
})
const client = ioclient.connect(`http://localhost:${NODE_PORT}`)
})
afterEach(() => {
this._ioserver.close()
this._http.close()
})
})
Any ideas why that happens?
So, the problem was, that the server closed the client connection on a successful connection event. The client did not get any information on that, but instead saw a failed connection and tried to reconnect. This opened a socket to the server again and because the server was already closed, the connection error kept coming.
This behavior stopped node from properly destroying all objects, which in turn explaines the hanging. The solution is to call done() only after the client has declared a connection open, not after the server has declared a connection open like so:
'use strict'
const Express = require('express')
const Http = require('http')
const ioserver = require('socket.io')
const ioclient = require('socket.io-client')
const NODE_PORT = process.env.NODE_PORT || 3000
describe('Client', function () {
beforeEach(() => {
const express = new Express()
this._http = Http.Server(express)
this._ioserver = ioserver(this._http)
this._http.listen(NODE_PORT)
this._client = null
})
it('should connect to a socketio-server', (done) => {
this._ioserver.on('connection', () => {
done()
})
this._client = ioclient.connect(`http://localhost:${NODE_PORT}`)
})
it('should finish the test suite even with a middleware', (done) => {
this._ioserver.use((socket, next) => {
return next()
})
this._client = ioclient.connect(`http://localhost:${NODE_PORT}`)
// if we wait for the server and kill the server socket,
// the client will try to reconnect and won't be killed
// by mocha.
this._client.on('connect', () => {
done()
})
})
afterEach(() => {
// this last call forces the client to stop connecting
// even if tests failed
this._client.close()
this._ioserver.close()
this._http.close()
})
})