We’re practitioners of test driven development. On a recent node.js project we had a need to mock the interactions of a few classes with MySql. We quickly turned to our trusty friend Sinon.js to get the test setup. We couldn’t find any good examples online, so we decided to make one.
TL;DR; Check it out on GitHub
In our example we have a UserRepository which depends on an instance of a Mysql connection. We injected the mysql dependecy through the constructor so it will be easy substitute a mock for testing.
Here is our UserRepository.js
'use strict';
var Promise = require('bluebird');
/**
* @constructor
* Instantiate a user repository class.
* @param {Object} mysqlConnection - the result of mysql.createConnection()
*/
function UserRepository(mysqlConnection) {
this.connection = mysqlConnection;
}
/**
* Fetch a user from the repository.
* @param {String} username
* @return {Promise.<UserRecord>}
*/
UserRepository.prototype.getUserByName = function(username) {
var self = this;
var query = 'select * from users where name = ?';
var params = [ username ];
var promise = new Promise(function(resolve, reject) {
self.connection
.query(query, params, function(error, results, fields) {
if (error) reject(new Error(error));
resolve(results[0]);
});
});
return promise;
}
module.exports = UserRepository;
At the beginning of our test suite we create an instance of a mysql connection. We pass in the options array with the host because it is required. We will not actually be opening a connection to Mysql. Next, we create a mock handle by passing the mysql connection into sinon.mock. We can use to mock to setup expectations on the mysql connection class. Finally we create an instance of our UserRepository class, injecting the mysql connection that we have a mock handle on.
This is our UserRepositoryTest.js
'use strict';
var chai = require('chai'),
should = chai.should(),
expect = chai.expect,
chaiAsPromised = require('chai-as-promised'),
Mysql = require('mysql'),
sinon = require('sinon'),
Promise = require('bluebird'),
UserRepository = require('./UserRepository.js');
chai.use(chaiAsPromised);
describe('UserRepository', function() {
var mysqlConnection = Mysql.createConnection({host: 'localhost'});
var mysqlMock = sinon.mock(mysqlConnection);
var userRepository = new UserRepository(mysqlConnection);
There are two tests in our example. The first is a test of a successful call to the getUserByName method.
Initially, we setup the test data and results. If you don’t know what test data to use, you can use the repl in the chrome inspector to query mysql and log the results. Next we use our mock handle to setup an expectation on our mysql connection. The .expects(‘query’) tell the mock to intercept the next call to the query method of the mysql connection. .withArgs(…) tells the mock what the argument to the next call to .query(…) should be. Then .callsArgWith(2, …) tells the mock handle to call the third argument of the .query(…) method as a function with the following arguments. Last, we call our getUserByName method and confirm that it returns the first value of the array we set up .query to return with .callsArgWith(…).
it('generates the proper select query when fetching a user and return promise', function() {
var name = 'john doe';
var results = [{ id: 0, name: name }];
var fields = ['id', 'name'];
var expectation = mysqlMock.expects('query')
.withArgs('select * from users where name = ?', [ name ])
.callsArgWith(2, null, results, fields);
return userRepository.getUserByName(name).should.eventually.become(results[0]);
});
In our second test we create an expectation to confirm that query gets called, and we mock in an error as the return value. Finally we assert that the promise returned from getUserByName call is rejected.
it('rejects promises when there is a mysql error', function() {
var expectation = mysqlMock.expects('query')
.once()
.callsArgWith(2, 'example error', null, null);
return userRepository.getUserByName('fred').should.be.rejectedWith('example error');
});
});
We hope you find this helpful!