javascriptnode.jsunit-testingnodeunit

Writing unit tests with DB connections/drivers with nodeunit


I am trying to write a set of test units for my simple DB layer SimpleDbLayer using nodeunit. I am finding it rather difficult.

Problems:

1) I want to connect to the DB once, and use that connection. I cannot really use setUp and tearDown, as they are run before and after each test. As a "solution", I am using a module-wide variable for the scope (see code below)

2) My simple DB layer has several DB drivers. MongoMixin is only one of them -- I have under development MariaMixin and PostgresMixin. Ideally I would run these tests one for each driver, to see if they work. Is there a good, easy way to do this?

3) I am hitting cases where I make a mistake with the code of my test, and all I get is a complaint about test.done not being detected. Is there any trickery I need to be aware of here?

4) Am I doing anything horribly wrong? (see code)

var 
  dummy

, declare = require('simpledeclare')
, SimpleDbLayer = require('./SimpleDbLayer')

, MongoMixin = require('./MongoMixin.js')

, mw = require('mongowrapper')
, async = require('async')
;

var db, layer;


exports.createLayer = {

  databaseConnect: function( test ){
    mw.connect('mongodb://localhost/hotplate', {}, function( err, returnedDb ){
      test.ifError( err );
      db = returnedDb;
      test.done( null );
    });
  },


  makeLayer: function( test ){

    var C = declare( [ SimpleDbLayer, MongoMixin ] );
    layer = new C( 'test', {  name: true, surname: true, age: true }, db );

    test.ok( layer );
    test.done(); 
  },

  deleteAll: function( test ){
    layer.delete( { }, { multi: true }, function( err, howmany ){
      test.ifError( err );
      test.done();
    });
  },

  insert: function( test ){
    var people = [
      { name: 'Chiara',    surname: 'Mobily',     age: 24 },
      { name: 'Tony',      surname: 'Mobily',     age: 37 },
      { name: 'Sara',      surname: 'Connor',     age: 14 },
      { name: 'Daniela',   surname: 'Mobily',     age: 64 },
    ];

    returnedPeople = [];

    var functions = [];

    // Populate the database
    people.forEach( function( person ){

      functions.push( function( done ){
        layer.insert( person, { returnRecord: true }, function( err, person ){
          test.ifError( err );
          returnedPeople.push( person );
          done( null );
        })
      })

    })

    async.series( functions, function( err, res ){
      test.ifError( err );
      test.done();
    });

  },

  databaseDisconnect: function( test ){
    db.close();
    test.done();
  },

} 

Solution

  • Answering myself. The solution is simple: for each driver, this is the test.js file:

    var driver = require('./specificDriver'); var simpledblayerTests = require( "./lib/simpledblayer/test.js" );

    var tests = simpledblayerTests.get(
    
      function getDbInfo( done ) {
        mw.connect('mongodb://localhost/tests', {}, function( err, db ){
          if( err ){
            throw new Error("MongoDB connect: could not connect to database");
          } else {
            done( null, db, driver );
          }
        });
      },
    
      function closeDb( db, done ) {
        db.close( done );
      }
    );
    
    
    for(var test in tests) {
        exports[ test ] = tests[ test ];
    }
    

    Basically, the get() function exported by the actual "main" module (more about this later) takes two parameters: two functions that open and close the database connection.

    That get() function returns a bunch of functions ready to be exported -- the functions that make up your unit testings.

    The main module will have something like this:

    exports.get = function( getDbInfo, closeDb ){
    
      var tests;
      var g = {};
    
      var startup = function( test ){
        var self = this;
    
        test.doesNotThrow( function(){
    
          getDbInfo( function( err, db, driver ){
            if( err ){
              throw( new Error("Could not connect to db, aborting all tests") );
              process.exit();
            }
    
            // Set the important g.driver variables (db and driver)
            g.db = db;
            d.driver = driver
    
            test.done();
          });
        });
      }
    
    
      var finish = function( test ){
        var self = this;
        closeDb( g.db, function( err ){
          if( err ){
            throw( new Error("There was a problem disconnecting to the DB") );
          }
          test.done();
        });
      };
    
      tests = {
    
        startup: startup,
    
        // ...
        // Your tests here. All functions will have access to g.db and g.driver 
        // ...
    
        finish: finish
    
      }
    
      return tests;
    }
    

    This way, every single module will run the same tests. However, the driver variable and the functions to connect/disconnect to the database will be simple.

    See it in action with real code: