Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to export node express app for chai-http

I have an express app with a few endpoints and am currently testing it using mocha, chai, and chai-http. This was working fine until I added logic for a pooled mongo connection, and started building endpoints that depended on a DB connection. Basically, before I import my API routes and start the app, I want to make sure I'm connected to mongo.

My problem is that I'm having trouble understanding how I can export my app for chai-http but also make sure there is a DB connection before testing any endpoints.

Here, I am connecting to mongo, then in a callback applying my API and starting the app. The problem with this example is that my tests will start before a connection to the database is made, and before any endpoints are defined. I could move app.listen and api(app) outside of the MongoPool.connect() callback, but then I still have the problem of there being no DB connection when tests are running, so my endpoints will fail.

server.js

import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;
MongoPool.connect((err, success) => {
    if (err) throw err;
    if (success) {
        console.log("Connected to db.")
        // apply express router endpoints to app
        api(app);
        app.listen(port, () => {
            console.log(`App listening on port ${port}`);
        })
    } else {
        throw "Couldnt connect to db";
    }

})
export default app;

How can I test my endpoints using chai-http while making sure there is a pooled connection before tests are actually executed? It feels dirty writing my application in a way that conforms to the tests I'm using. Is this a design problem with my pool implementation? Is there a better way to test my endpoints with chai-http?

Here is the test I'm running

test.js

let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server').default;;
let should = chai.should();


chai.use(chaiHttp);
//Our parent block
describe('Forecast', () => {
/*
  * Test the /GET route
  */
  describe('/GET forecast', () => {
      it('it should GET the forecast', (done) => {
        chai.request(server)
            .get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
            .end((err, res) => {
                res.should.have.status(200);
              done();
            });
      });
  });

});

And this is the endpoint I'm testing

/api/forecast.js

import express from 'express';
import MongoPool from '../lib/MongoPool';
let router = express.Router();
let db = MongoPool.db();

router.get('/forecast', (req, res) => {
    // do something with DB here
})

export default router;

Thank you for any help

like image 380
Isaac Vidrine Avatar asked Mar 15 '19 05:03

Isaac Vidrine


People also ask

How do I get the HTTP server from the Express App?

The usual two ways to start a server for use with Express are this: var express = require('express'); var app = express(); // Express creates a server for you and starts it var server = app. listen(80);

What is chai HTTP in node JS?

Chai HTTP provides an interface for live integration testing via superagent. To do this, you must first construct a request to an application or url. Upon construction you are provided a chainable api that allows you to specify the http VERB request (get, post, etc) that you wish to invoke.


2 Answers

After receiving some good feedback, I found this solution works best for me, based on Gomzy's answer and Vikash Singh's answer.

In server.js I'm connecting to the mongo pool, then emitting the 'ready' event on the express app. Then in the test, I can use before() to wait for 'ready' event to be emitted on the app. Once that happens, I'm good to start executing the test.

server.js

import express from 'express';
import bodyParser from 'body-parser';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 5000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

(async () => {
    await MongoPool.connect();
    console.log("Connected to db.");
    require('./api').default(app);
    app.listen(port, () => {
        console.log(`Listening on port ${port}.`)
        app.emit("ready");
    });
})();

export default app;

test.js

//Require the dev-dependencies
import chai from 'chai';
import chaiHttp from 'chai-http';
import server from '../src/server'; 
let should = chai.should();
chai.use(chaiHttp);

before(done => {
  server.on("ready", () => {
    done();
  })
})
describe('Forecast', () => {
  describe('/GET forecast', () => {
    it('it should GET the forecast', (done) => {
      chai.request(server)
          .get('/api/forecast?type=grid&lat=39.2667&long=-81.5615')
          .end((err, res) => {
              res.should.have.status(200);
            done();
          });
    });
  });

});
like image 144
Isaac Vidrine Avatar answered Oct 14 '22 10:10

Isaac Vidrine


Express app is an instance of EventEmitter so we can easily subscribe to events. i.e app can listen for the 'ready' event.

Your server.js file will look like below,

import express from 'express';
import api from './api';
import MongoPool from './lib/MongoPool';
let app = express();
let port = process.env.PORT || 3000;

app.on('ready', function() {
  app.listen(3000, function() {
    console.log('app is ready');
  });
});

MongoPool.connect((err, success) => {
  if (err) throw err;
  if (success) {
    console.log('Connected to db.');
    // apply express router endpoints to app
    api(app);

    // All OK - fire (emit) a ready event.
    app.emit('ready');
  } else {
    throw 'Couldnt connect to db';
  }
});

export default app;
like image 36
Gautam Avatar answered Oct 14 '22 10:10

Gautam