Julian Veerkamp
Julian Veerkamp

Reputation: 1780

Keep MongoDB connection open while running tests using mocha framework

I'm using something akin to How to properly reuse connection to Mongodb across NodeJs application and modules to keep my mongoDB Connection open.
This requires all the code to go into a MongoDB.connectDB(async (err) => {...} Block.
How would I use this when writing Tests with Mocha.
Do I have to use a separate Connection for every test? Like this?

const MongoDB = require('../src/mongoUtil') // providing access to the mongo database
var events = require('../src/events') // containing all my database Functions

describe('events.js', function () {
  describe('addEvent()', function () {
    it('should return the event, when added succesfully', async function () {
      await MongoDB.connectDB(async (err) => {
        if (err) throw err

        const database = MongoDB.getDB()
        const eventsCollection = database.db().collection('events')

        const event = {
          name: 'test Event',
          members: []
        }

        const result = await events.addEvent(eventsCollection, event)

        MongoDB.disconnectDB()

        if (result.name !== event.name) {
          return new Error('TODO')
        }
      })
    })
  })
})

For this example to work properly I probably also have to manually set the test to done, if I understood the mocha website correctly?
Can I have just one connection?

mongoUtil.js

const MongoClient = require('mongodb').MongoClient
const uri = 'mongodb://localhost:27017/testing'

let _db

const connectDB = async (callback) => {
  try {
    MongoClient.connect(uri, { useNewUrlParser: true }, (err, db) => {
      _db = db
      return callback(err)
    })
  } catch (e) {
    throw e
  }
}

const getDB = () => _db

const disconnectDB = () => _db.close()

module.exports = { connectDB, getDB, disconnectDB }

events.js

const addEvent = async (collection, event) => {
  try {
    const exists = await collection.findOne({ 'name': event.name })
    if (exists) {
      return false
    } else {
      const results = await collection.insertOne(event)
      return results.ops[0]
    }
  } catch (e) {
    throw e
  }
}
module.exports = { addEvent }

Upvotes: 1

Views: 941

Answers (1)

Estus Flask
Estus Flask

Reputation: 222750

You don't need to put everything inside MongoDB.connectDB({...}). This is called callback hell which should be avoided when possible. mongoUtil is inconvenient wrapper that doesn't make MongoDB API simpler or easier to use.

await MongoDB.connectDB(async (err) => { ... is a mistake. Putting async function as a callback is not enough and will result in improper control flow.

MongoClient.connect uses error-first callbacks, so it can be used with done to pass errors to test suite:

let db;

beforeEach(done => {
  MongoClient.connect(uri, { useNewUrlParser: true }, (err, _db) => {
    db = _db;
    done(err);
  });
});

connectDB incorrectly promisifies MongoClient.connect. There's no need for one-time callbacks when promises and async..await are in use. It could be:

const connectDB = () => {
  return new Promise((resolve, reject) => {
    MongoClient.connect(uri, { useNewUrlParser: true }, (err, db) => {
      if (err) reject(err);
      else resolve(db);
    });
  });
};

But the fact is that mongodb supports promises natively when callback argument is omitted. mongoUtil needs only one helper function, connectDB:

const connectDB = () => MongoClient.connect(uri, { useNewUrlParser: true });

Which can be used with Mocha seamlessly since it supports promises:

let db;

beforeEach(async () => {
  db = await connectDB();
});

afterEach(() => db.close());

close is asynchronous and returns a promise, too.

Upvotes: 2

Related Questions