How do you mock MySQL (without an ORM) in Node.js?

I'm using Node.js with felixge's node-mysql client. I am not using an ORM.

I'm testing with Vows and want to be able to mock my database, possibly using Sinon. Since I don't really have a DAL per se (aside from node-mysql), I'm not really sure how to go about this. My models are mostly simple CRUD with a lot of getters.

Any ideas on how to accomplish this?


Solution 1:

With sinon, you can put a mock or stub around an entire module. For example, suppose the mysql module has a function query:

var mock;

mock = sinon.mock(require('mysql'))
mock.expects('query').with(queryString, queryParams).yields(null, rows);

queryString, queryParams are the input you expect. rows is the output you expect.

When your class under test now require mysql and calls the query method, it will be intercepted and verified by sinon.

In your test expectation section you should have:

mock.verify()

and in your teardown you should restore mysql back to normal functionality:

mock.restore()

Solution 2:

It may be a good idea to abstract away your database into its own class which uses mysql. Then you can pass that class' instance to your model's constructors instead of them loading it using require().

With this set up you can pass a mock db instance to your models inside your unit test files.

Here's a small example:

// db.js
var Db = function() {
   this.driver = require('mysql');
};
Db.prototype.query = function(sql, callback) {
   this.driver... callback (err, results);
}
module.exports = Db;

// someModel.js
var SomeModel = function (params) {
   this.db = params.db
}
SomeModel.prototype.getSomeTable (params) {
   var sql = ....
   this.db.query (sql, function ( err, res ) {...}
}
module.exports = SomeModel;

// in app.js
var db = new (require('./db.js'))();
var someModel = new SomeModel ({db:db});
var otherModel = new OtherModel ({db:db})

// in app.test.js
var db = {
   query: function (sql, callback) { ... callback ({...}) }
}
var someModel = new SomeModel ({db:db});

Solution 3:

I'm not entirely familiar with node.js, but in a traditional programming sense, to achieve testing like that, you'd need to abstract away from the data access method. Couldn't you create a DAL class like:

var DataContainer = function () {
}

DataContainer.prototype.getAllBooks = function() {
    // call mysql api select methods and return results...
}

Now in the context of a test, patch your getAllBooks class during initialization like:

DataContainer.prototype.getAllBooks = function() {
    // Here is where you'd return your mock data in whatever format is expected.
    return [];
}

When the test code is called, getAllBooks will be replaced with a version that returns mock data instead of actually calling mysql. Again, this is a rough overview as I'm not entirely familiar with node.js