demouser123
demouser123

Reputation: 4264

Mocha timeouts during MongoDB connection

Mongo Novice here. I am trying to connect a unit test my mongo db collections using Mocha, Chai. But every time I run the test, the connection seems to time out. I have increased timeout in mocha.opts to 50K ms but still the connection seems to time out. I'm unable to get the reason why?

Here is my code

use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const chai = require('chai');
const expect = chai.expect;



/**
 * 
 * Create a new schema that accepts a firstname and lastname and employee id
 */

 const testSchema = new Schema({
     firstname : {type: String,required:true},
     lastname : {type: String, required:true},
     id : {type : Number, required : true}
 });

 /**
  * 
  * Create a new collection name employee_details 
  */

  const employee_details = mongoose.model('employee_details',testSchema);

  describe('Create a connection with the database',()=>{
      before((done)=>{
          mongoose.connect('mongodb://127.0.0.1:27017/new_demo');
   //I tried changing the url to mongodb://localhost/new_demo but it didn't work
          const db  = mongoose.connection;
          db.on('error',console.error.bind(console,'Error connecting to DB'));
          db.once('open',()=>{
              console.log('Connected to new_demo db');
              done();
          });
      });

  });


  describe('Test Database function',()=>{
      //Save something with value Mike Stevens, 19981
      it('saves a new record',(done)=>{
        var first_record = employee_details({
            firstname : 'Mike',
            lastname : 'Stevens',
            id : 19981
        });

        first_record.save(done);
      });



      after((done)=>{
          mongoose.connection.db.dropDatabase(()=>{
              mongoose.connection.close(done);
          });
      });
  });

Things I have tried so far

The error message is

Error: Timeout of 50000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/ab/ab/abcd/api-testing/mongo-testing/my-mongo-tests/test/create_connection.js)

As per the error message, the done should be called, which is done in the code. Then why this error?

Edit 1 : Full error message

Test Database function
    1) saves a new record
    2) "after all" hook


  0 passing (50s)
  2 failing

  1) Test Database function
       saves a new record:
     Error: Timeout of 50000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/ab/ab/abcd/api-testing/mongo-testing/my-mongo-tests/test/create_connection.js)


  2) Test Database function
       "after all" hook:
     TypeError: Cannot read property 'dropDatabase' of undefined
      at Context.after (test/create_connection.js:56:34)

Upvotes: 1

Views: 1279

Answers (1)

damitj07
damitj07

Reputation: 2899

A mocha test case is no different than normal JS code, thus it will follow the scope isolation and flow of JS code. I have tried to fix the test case to allow variable access in various test cases and before/after hooks.

Please note that I have not executed the actual test case and you may need to modify the code below to make it run successfully for you

'use strict';
 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const chai = require('chai');
 const expect = chai.expect;



 /**
  * 
  * Create a new schema that accepts a firstname and lastname and employee id
  */
 const testSchema = new Schema({
     firstname: { type: String, required: true },
     lastname: { type: String, required: true },
     id: { type: Number, required: true }
 });

 /**
  * 
  * Create a new collection name employee_details 
  */
 const employee_details = mongoose.model('employee_details', testSchema);

 /**
  * Decalre the db const in this(global) context so this variable is visible by all test cases
  */
 const db;

 //  describe('Create a connection with the database', () => {
 //      // Seems redundet to something
 //      // Also the context is lost if you do this .. 
 //      // Thus not allowing for you to use variables .. const db in this case
 //      // If you still want this describe, you will have to use it like any other javascript function ... to expose the variables
 //  });


 describe('Test Database function', () => {

     // connect to database     
     before((done) => {
         mongoose.connect('mongodb://127.0.0.1:27017/new_demo');
         //Keep the url same which you use to debug you local application
         db = mongoose.connection;
         db.on('error', console.error.bind(console, 'Error connecting to DB'));
         db.once('open', () => {
             console.log('Connected to new_demo db');
             done();
         });
     });

     //Save something with value Mike Stevens, 19981
     it('saves a new record', (done) => {
         // Also if you want to increase the deafult timeout of a teast case
         // you will have to change the => to function(), because of the way 'this' context behaves
         // Thus : 
         //   it('saves a new record', function(done) {
         //      this.timeout(10000);
         //      .. test case code
         //   }
         var first_record = employee_details({
             firstname: 'Mike',
             lastname: 'Stevens',
             id: 19981
         });

         first_record.save(function(err) {
             if (err) return handleError(err);
             // saved!
             done();
             //I used a simple callback function instead, makes life easier and code understable
         })
     });



     after((done) => {
         mongoose.connection.db.dropDatabase(() => {
             mongoose.connection.close(done);
             // I am guessing same goes here .. 
             // keep it simple !!
         });
     });
 });

Now some theory, Ideally it is not recommend or rather not in the scope of a unit test case to actually connect to a database and/or modify the state of the external entity in any way (since you specifically mentioned that 'it is a unit test case').

A unit test should avoid making external calls or invoke actual API's. We should stub the call and in our test case assert that the call was made when we expected it to be, or when the appropriate input was provided.

Here's an example to walk the talk :

//This is myGLobalServiceLoactor which is used in actual code which is to be tested.
 myGLobalServiceLoactor = {
     database: {
         save: sinon.stub(),
         find: sinon.stub()
     }
 }

 it('to check if external method is called ', () => {
     let person_to_Save = {
         //. . . 
         //. . . 
     }
     proxyPersonInterface.savePerson(input_person).then((status) => {
         // check if our stubbeb function is called
         assert(myGLobalServiceLoactor.database.save.calledOnce);
     });

 });

And you can use helper libraries like Sinon and rewire to stub and proxy the actual modules in a unit test case. Hope that helps.

Upvotes: 1

Related Questions