'app.address is not a function' when testing an express HTTPS server - node.js

I am running into issues when testing my express application. All tutorials use app.listen instead of https.createServer and I don't know how to properly use testing frameworks with the latter. The code:
test_node/app.js
var express = require("express");
const app = express();
app.set('port', process.env.SERVER_PORT || 443);
app.get('/', function(req, res){
res.status(200).send('OK');
});
module.exports = {
app: app
}
test_node/server.js
var https = require('https');
var fs = require('fs');
const app = require('./app');
let serverOptions = {
key: fs.readFileSync('./cert/server.key'),
cert: fs.readFileSync('./cert/server.cert')
}
const server = https.createServer(serverOptions, app)
server.listen(app.get('port'), function () {
console.log(`Express HTTPS server listening on port ${app.get('port')}`);
});
async function handleSignal(signal) {
console.log(`Received ${signal}`);
server.close();
process.exit();
}
process.on('SIGINT', handleSignal);
process.on('SIGTERM', handleSignal);
process.on('uncaughtException', handleSignal);
test_node/test/test.js
const request = require('supertest')
describe("Test the root path", () => {
let server;
beforeAll(function () {
server = require('../app');
});
afterAll(function () {
server.close();
});
test("It should respond to the GET method", async () => {
const response = await request(server).get("/");
expect(response.statusCode).toBe(200);
});
});
test_node/package.json
{
"name": "test_node",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "jest"
},
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^26.6.3",
"supertest": "^6.1.3"
},
"dependencies": {
"express": "^4.17.1"
}
}
The error I get when I test the above code with jest (jest --runInBand test.js):
TypeError: app.address is not a function
at Test.Object.<anonymous>.Test.serverAddress (C:\code\test_node\node_modules\supertest\lib\test.js:57:18)
at new Test (C:\code\test_node\node_modules\supertest\lib\test.js:38:12)
at Object.get (C:\code\test_node\node_modules\supertest\index.js:27:14)
at Object.<anonymous> (C:\code\test_node\test\test.js:13:48)
at Object.asyncJestTest (C:\code\test_node\node_modules\jest-jasmine2\build\jasmineAsyncInstall.js:106:37)
at C:\code\test_node\node_modules\jest-jasmine2\build\queueRunner.js:45:12
at new Promise (<anonymous>)
at mapper (C:\code\test_node\node_modules\jest-jasmine2\build\queueRunner.js:28:19)
at C:\code\test_node\node_modules\jest-jasmine2\build\queueRunner.js:75:41
This is obviously a stripped down version of my code. I also create MongoDB connections at start, but I removed them for simplicity. Ideally I want to use async/await instead of promises for creating my server (and MongoDB connections) before my tests start. I also need to close those connections after tests end or fail. In your answer you can safely assume I'm JS beginner :).
Additional info
I'm using node '14.15.4' on Windows 10 x64. Webstorm IDE.
Tried various combinations of the following:
combining app and server in one file (see below)
creating the server in a separate function and exporting it
using mocha/chai instead of jest
testing a stripped down version of the app
Errors I get depending on the combinations I tried:
TypeError: app.address is not a function
TypeError: app.get is not a function
EADDRINUSE: address already in use ::::443
various other errors with timeouts, wrong arguments, etc.
Relevant topics that didn't help (or I overlooked something):
Simple node http server unit test
Is it possible to create an Express.js server in my Jest test suite?
TypeError: app.get is not a function
Mocha API Testing: getting 'TypeError: app.address is not a function'
Getting "TypeError: "listener" argument must be a function" in Node.Js
Jest/Supertest errors with TypeError: app.address is not a function
https://blog.campvanilla.com/jest-expressjs-and-the-eaddrinuse-error-bac39356c33a
https://glebbahmutov.com/blog/how-to-correctly-unit-test-express-server/
https://rahmanfadhil.com/test-express-with-supertest/
https://zellwk.com/blog/endpoint-testing/
https://taylor.fausak.me/2013/02/17/testing-a-node-js-http-server-with-mocha/
https://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/
Context
I included the server.js file above, although the test doesn't use it, because my server application should also be able to start normally when I run node server.js. With the above code it doesn't start and I get the following error:
events.js:112
throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
^
TypeError [ERR_INVALID_ARG_TYPE]: The "listener" argument must be of type function. Received an instance of Object
at checkListener (events.js:112:11)
at _addListener (events.js:348:3)
at Server.addListener (events.js:406:10)
at new Server (https.js:71:10)
at Object.createServer (https.js:91:10)
at Object.<anonymous> (C:\code\test_node\server.js:9:22)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14) {
code: 'ERR_INVALID_ARG_TYPE'
}
However, if I put all code in the same app.js file and I run node app.js, then the app starts fine, although the test produces the same error.
test_node/app.js
var https = require('https');
var fs = require('fs');
var express = require("express");
var server;
const app = express();
app.set('port', process.env.SERVER_PORT || 443);
app.get('/', function(req, res){
res.status(200).send('OK');
});
async function handleSignal(signal) {
console.log(`Received ${signal}`);
server.close();
process.exit();
}
process.on('SIGINT', handleSignal);
process.on('SIGTERM', handleSignal);
process.on('uncaughtException', handleSignal);
let serverOptions = {
key: fs.readFileSync('./cert/server.key'),
cert: fs.readFileSync('./cert/server.cert')
}
server = https.createServer(serverOptions, app).listen(app.get('port'), function () {
console.log(`Express HTTPS server listening on port ${app.get('port')}`);
});
module.exports = {
app: app,
server: server
}
To clarify, my question is how to fix the error from the jest test and not about the error when I run node server.js - the latter is just for context.

As #jonrsharpe pointed out in the comments, I was assuming that with module.exports I export the app itself, but in fact I export an object containing the app. Therefore to fix the test error, I had to simply write in my test.js:
server = require('../app').app;
instead of server = require('../app');. Similarly, in server.js I had to write:
const app = require('./app').app;
This fixes both errors I mentioned in my question (TypeError: app.address is not a function).
Credits to #jonrsharpe for helping me find the solution!

Related

Node Application test via jest and supertest keep failing

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();

JEST with Supertest getting instance already in use error - need to restart the whole test 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.

EADDRINUSE when watching tests with mocha and supertest

I'm trying to do BDD tests to cover my code, but I got this error :
I'm trying to do BDD tests to cover my code, but I got this error :
I'm trying to do BDD tests to cover my code, but I got this error :
I'm trying to do BDD tests to cover my code, but I got this error :
I'm trying to do BDD tests to cover my code, but I got this error :
app.js
"use strict";
var app = require("express")();
var pg = require("pg");
var http = require("http");
var https = require("https");
require("./config/config");
var server;
swaggerTools.initializeMiddleware(swaggerConfig, function (middleware) {
// Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
app.use(middleware.swaggerMetadata());
var sayHello = ( type ) => {
return ( ) => {
console.log( `${global.gConfig.app.name} (${global.gConfig.app.desc}) listening for ${type.toUpperCase()} connections on port ${global.gConfig.ports[type]}`);
}
}
if( global.gConfig.ports.http ) {
http.createServer(app).listen( global.gConfig.ports.http, sayHello('http') );
server=app.listen(global.gConfig.ports.http);
}
if( global.gConfig.ports.https ) {
server=app.listen(global.gConfig.ports.https);
}
});
module.exports = {
serve:server,
app:app
};
test.js
let chai = require('chai');
let expect = require('chai').expect;
let chaiHttp = require('chai-http');
var app=require('../').app;
var server=require('../').server;
let should = require('chai').should;
var request = require("supertest").agent(server);
chai.use(chaiHttp);
describe('Caracteristiques', () => {
after(function (done) {
server.close();
done();
});
it('returns an array of Carateristiques', (done) => {
//chai.request(server)
request
.get('caracteristiques')
.set('Authorization', 'Bearer token')
.end((err, res) => {
console.log("res :",res);
expect(res.status).to.equal(200);
done();
});
});
})
the error :
1) Uncaught error outside test suite:
Uncaught Error: listen EADDRINUSE :::3001
at Object._errnoException (util.js:992:11)
at _exceptionWithHostPort (util.js:1014:20)
at Server.setupListenHandle [as _listen2] (net.js:1355:14)
at listenInCluster (net.js:1396:12)
at Server.listen (net.js:1480:7)
at Function.listen (node_modules\express\lib\application.js:618:24)
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\app.js:75:17
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\node_modules\swagger-tools\index.js:85:7
at cbWrapper (node_modules\swagger-tools\lib\specs.js:1023:5)
at validateSwagger2_0 (node_modules\swagger-tools\lib\specs.js:1018:3)
at validateSemantically (node_modules\swagger-tools\lib\specs.js:1028:5)
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\node_modules\swagger-tools\lib\specs.js:1221:7
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\node_modules\swagger-tools\lib\specs.js:1061:29
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\node_modules\swagger-tools\lib\specs.js:707:12
at C:\Users\zya\Documents\Tdbc-api\tdbc-api\node_modules\swagger-tools\lib\specs.js:683:9
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at startup (bootstrap_node.js:191:16)
at bootstrap_node.js:612:3
2) Caracteristiques
returns an array of Carateristiques:
TypeError: Cannot read property 'address' of undefined
at Test.serverAddress (node_modules\supertest\lib\test.js:55:18)
at new Test (node_modules\supertest\lib\test.js:36:12)
at TestAgent.(anonymous function) [as get] (node_modules\supertest\lib\agent.js:52:15)
at Context.it (test\caracteristiques.js:31:14)
3) Caracteristiques
"after all" hook:
TypeError: Cannot read property 'close' of undefined
at Context.<anonymous> (test\caracteristiques.js:23:16)
I think the set up id good, can any one helop please ?
i think it's not working because you are trying to enable the server in the same port each time your test is executed. [Or you have your sever running while you execute your tests].
Try to shut down your server while you execute your tests.
FYI: You are exporting your server main module as "serve", then you try to acceed to it using require(path).server instead of require(path).serve;

NodeJS: Error found: listen EADDRINUSE :::3000?

I'm currently facing a bug that I can't resolve and I have been struggling for some hours.
I'm using the following versions:
Node: 8.11.3
Express: 4.16.3
Jest: 22.2.2
Mongoose: 5.2.3
I'm trying to do some integration tests with jest and I have 2 files with tests.
In each file I wrote the following:
// Create the server before each test.
beforeEach(() => {
server = require('../../index');
});
// Close the server after each test.
afterEach(async () => {
if (server) {
server.close();
}
});
In index.js I have the following (This is not all the code, but the relevant code for the bug):
// Listen to the server.
const port = config.PORT || process.env.PORT || 3000;
module.exports = app.listen(port, () => {
winston.info(`Listening to port ${port}...`);
});
When I run npm test
I get this exception all the time:
**listen EADDRINUSE :::3000**
10 | // Listen to the server
11 | const port = config.PORT || process.env.PORT || 3000;
> 12 | module.exports = app.listen(port, () => {
13 | winston.info(`Listening to port ${port}...`);
14 | });
15 |
I tried several ways to solve this by adding async await to beforeEach and for afterEach and tried also to put the sever.close in afterAll and in beforeAll but still got the same error.
Then, I tried to solve by doing this:
How Do I Shut Down My Express Server Gracefully When Its Process Is Killed?
But again, with no luck.
Finally, when I wrote all the tests in 1 file, it works without this error. Does anybody know how to solve this? I don't want to write all my integration tests in 1 file..
Thanks!
Try setting the --runInBand flag, this will run your tests sequentially.
https://jestjs.io/docs/en/cli#runinband
In package.json scripts:
"scripts": {
...
"test": "jest --watchAll --verbose --runInBand",
...
}
[UPDATE] On further investigation, while you can use --runInBand to run your tests sequentially, another option is to await the server.close() since it returns a promise. As such, in your afterEach:
...
await server.close();
...
[UPDATE] I believe a better way to solve this issue is to separate out the index.js file into an index.js and a server.js file. This enables you to pull your index.js file into your tests without .listen:
// index.js
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.status(200).send({ hello: "world!" });
});
module.exports = app;
Then in a separate server.js file:
const server = require("./index");
const PORT = process.env.PORT || 3001;
server.listen(PORT, () => {
console.log(`App running on: ${PORT}`);
});
module.exports = server;
When running your app, you run: node server.js.
This then enables you to test your routes by pulling your index.js file into your test as follows:
// test.js
const request = require("supertest");
const server = require("./index");
describe("GET /", () => {
test("should return status 200", async () => {
const res = await request(server).get("/");
expect(res.status).toBe(200);
});
});
I am following the same course and had the same issue. --runInBand solved it for me.
The issue here is requiring server file multiple times. You shouldn't require server file like that. Each time you do that, an instance starts, in your first test file an instance of server already started, while requiring server for your second file, it tries to start another instance, but then your server is already running on 3000 and hence the error.
I think your port 3000 is busy somewhere else Or maybe this app is running on somewhere else in the terminal, Check it and close all the instances of this app.
Or
Try to use below code:
// Listen to the server
11 | const port = config.PORT || process.env.PORT || 3001; // CHANGE POST 3000-> 3001
> 12 | module.exports = app.listen(port, () => {
13 | winston.info(`Listening to port ${port}...`);
14 | });

How to test app connection with express, nodeJS and Mocha

I've got my app properly configured, such that if I run node app.js everything starts up. I'm trying to test for the connection of my app is working properly using unit tests. What I have so far is:
var options = {
url: 'http://localhost',
port: 8080,
};
var app = require("../app.js");
var should = require('should');
var assert = require("assert");
var async = require('async');
it ('tests the ok connection of the app by checking that it is listening on port 8080', function(dont) {
request(options,app)
.get('/')
.expect(200)
.end(function (err, res) {
res.header['location'].should.include('/')
res.text.should.include('Listening on port 8080');
done();
});
});
I was getting the error message Error: options.uri is a required argument, which is why I added the var options. Now I'm getting error message TypeError: Object #<Request> has no method 'get'.
How can properly check that I have a good connection (200), that i'm on the right page ('/') and that it is logging the message Listening on port 8080 upon connection?
Thanks!
For the TypeError, make sure you are using supertest and not something else like request. The reason for this is that the module request does not provide the .expect() and other functions.

Resources