Stubbing Redis interactions in javascript using Sinon - javascript

I am working in node.js. My app interacts with Redis via the node_redis module. I'm using mocha and sinon to automate testing of my app. My app looks something like this:
...snip
var redisClient = redis.createClient(redisPort, redisHost);
var someValue = redisClient.get("someKey");
return someValue;
....
I want to stub the call to redisClient.get(). To do this I also need to stub the call to redis.createClient() - I think... Here's my test code:
...
var redis = require("redis");
var redisClient;
...
sinon.stub(redisClient, 'get').returns("someValue");
sinon.stub(redis, "createClient").returns(redisClient);
...
assert.equal(redis_client_underTest.call_to_redis(), "someValue");
...
The test fails with AssertionError: false == "someValue"
How do I stub out redisClient, or is this even possible?

What you could do is use something like Proxyquire or Rewire. I'll be using rewire for the example.
Your snippet of code you want to stub:
var redisClient = redis.createClient(redisPort, redisHost);
var someValue = redisClient.get("someKey");
return someValue;
Then in your test you can use rewire:
var Rewire = require('rewire');
var myModule = Rewire("../your/module/to/test.js");
var redisMock = {
get: sinon.spy(function(something){
return "someValue";
});
};
myModule.__set__('redisClient', redisMock);
This way you can have your redisClient replaced and you can check with the spy if the function was called.

Few key points are:
Stub redisClient before loading main module.
Stub the CRUD methods for redisClient.
Below is my main module:
/* Main module */
const redis = require('redis');
const redisClient = redis.createClient();
function value(){
return redisClient.get('someKey');
}
module.exports = {value: value}
Below is my test script:
/* Test */
var sinon = require('sinon');
var redis = require('redis');
// stub redis.createClient
var redisClient = {
'get': () => "someValue"
}
var redisGetSpy = sinon.spy(redisClient, "get");
var redisClientStub = sinon.stub(redis,
"createClient").callsFake(() => redisClient);
// require main module
var main = require('./main.js');
console.log(main.value(),
redisClientStub.called,
redisGetSpy.called,
redisGetSpy.callCount);

The other way is to do in your class static function getRedis and mock it. For example:
let redis = {
createClient: function() {},
};
let connection = {
saddAsync: function() {},
spopAsync: function() {},
};
let saddStub = sinon.stub(connection, 'saddAsync');
sinon.stub(redis, 'createClient').returns(connection);
sinon.stub(Redis, 'getRedis').returns(redis);
expect(saddStub).to.be.calledOnce;
Inside your class connect function looks like:
this.connection = YourClass.getRedis().createClient(port, host, optional);

You can also use something like redis-mock which is a drop in replacement for node_redis that runs in memory.
Use rewire (or Jest module mocks or whatever) to load the mock client in your tests instead of the real client and run all your tests against the in-memory version.

This initially seemed very trivial to me, but I was faced with a lot of caveats, particularly because I was using jest for testing, which does not supports proxyquire. My objective was to mock the request dependency of redis, so here's how I achieved it:
// The objective is to mock this dependency
const redis = require('redis');
const redisClient = redis.createClient();
redis.set('key','value');
Mocking dependency with rewiremock:
const rewiremock = require('rewiremock/node');
const app = rewiremock.proxy('./app', {
redis: {
createClient() {
console.log('mocking createClient');
},
set() {
console.log('mocking set');
}
},
});
The catch is that you can't use rewire, or proxyquire with jest, that you need a library like rewiremock to mock this dependency.
REPL example

const sinon = require('sinon');
const redis = require('redis');
const { mapValues, isObject, isUndefined, noop, isFunction } = require('lodash');
let redisStub;
let redisStore = {};
const removeKeyWhenExpired = (db, key, expirationSec) => {
setTimeout(() => {
redisStore[db || 0][key] = undefined;
}, expirationSec * 1000);
};
sinon.stub(redis, 'createClient').callsFake(() => ({
on: () => { },
select: (db) => {
this.db = db;
redisStore[db] = {};
},
get: (key, callback) => callback(null, redisStore[this.db || 0][key]),
set: (key, value, ...args) => {
redisStore[this.db || 0][key] = isObject(value) ? JSON.stringify(value) : value;
if (args[0] === 'EX') {
removeKeyWhenExpired(this.db, key, args[1]);
}
const callback = args[args.length - 1] || noop;
if (isFunction(callback)) {
callback(null, true);
}
},
del: (key, callback = noop) => {
if (!isUndefined(redisStore[this.db || 0][key])) {
redisStore[this.db || 0][key] = undefined;
return callback(null, 1);
}
return callback(null, 0);
},
expireat: (key, exp, callback = noop) => {
removeKeyWhenExpired(this.db, key, exp - (new Date().getTime() / 1000));
return callback(null);
},
}));

Related

Unable to mock "module.exports" function with Sinon

I am trying to test my index.js file that is setup like the following.
However, when I debug the test, the config object is not being mocked properly.
Is there something that needs to be refactored or am I stubbing incorrectly?
File src/package/index.js:
let config = require("./config").get();
exports.handler = (event, context, callback) => {
// Expecting this object to be mocked.
config.delivery.forEach(delivery => {
...
})
};
File src/package/config.js:
const outoutObject = {
delivery: [{"test": "foo"}]
};
exports.get = () => outoutObject;
File test/test.js:
const handler = require("../src/package/index").handler;
const sinon = require("sinon");
const config = require("../src/package/config");
describe('test stubbing config', function() {
sinon.stub(config, "get").returns("stub");
handler({}, context, () => {});
})

How to mock pg Pool with Sinon

In a previous project I mocked the mysql library with Sinon. I did this like so:
X.js:
const con = mysql.createPool(config.mysql);
...
Some other place in the project:
const rows = await con.query(query, inserts);
...
X.test.js:
const sinon = require('sinon');
const mockMysql = sinon.mock(require('mysql'));
...
mockMysql.expects('createPool').returns({
query: () => {
// Handles the query...
},
...
It worked perfectly.
In another project I am trying to mock pg, again with Sinon.
pool.js:
const { Pool } = require('pg');
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
module.exports = pool;
Some other place in the project:
const con = await pool.connect();
const result = await con.query(...
Y.test.js:
???
I can't understand how to mock connect().query(). None of the following approaches work:
1:
const { Pool } = require('pg');
const config = require('#blabla/config');
const mockPool = sinon.mock(new Pool(config.get('database')));
...
mockPool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
1 results in no error but the real db connection is used.
2:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = new Pool(config.get('database'));
pool.expects('connect').returns({
query: () => {
console.log('query here');
},
});
2 => TypeError: Pool is not a constructor
3:
const { Pool } = sinon.mock(require('pg'));
const config = require('#blabla/config');
const pool = sinon.createStubInstance(Pool);
pool.connect.returns({
query: () => {
console.log('query here');
},
});
3 => TypeError: The constructor should be a function.
Can anybody point me in the right direction with how to mock my PostgreSQL connection?
Example: I have postgres.js like this.
const { Pool } = require('pg');
const handler = {
count: async (pgQuery) => {
try {
const pool = new Pool();
const res = await pool.query(pgQuery);
return { count: parseInt(res.rows[0].counter, 10) };
} catch (error) {
// Log/Throw error here.
}
return false;
}
}
module.exports = handler;
The spec test I created on postgres.spec.js is like this.
const { expect } = require('chai');
const sinon = require('sinon');
const pgPool = require('pg-pool');
const handler = require('postgres.js');
describe('Postgres', function () {
it('should have method count that bla bla', async function () {
// Create stub pgPool query.
const postgreeStubQuery = sinon.stub(pgPool.prototype, 'query');
postgreeStubQuery.onFirstCall().throws('XXX');
postgreeStubQuery.onSecondCall().resolves({
rows: [{ counter: 11 }],
});
// Catch case.
const catcher = await handler.count('SELECT COUNT()..');
expect(catcher).to.equal(false);
expect(postgreeStubQuery.calledOnce).to.equal(true);
// Correct case.
const correct = await handler.count('SELECT COUNT()..');
expect(correct).to.deep.equal({ count: 11 });
expect(postgreeStubQuery.calledTwice).to.equal(true);
// Restore stub.
postgreeStubQuery.restore();
});
});
To stub pool.query(), you need to stub pg-pool prototype and method query.
Hope this helps.
Since you're needing to mock the returned results of a query, I think the easiest solution would be to abstract your database from the the code needing the query results. Example being, your query results are returning information about a person. Create a person.js module with specific methods for interacting with the database.
Your other code needing the person information from the database won't know or care what type of database you use or how you connect to it, all they care to know is what methods are exposed from person.js when they require it.
//person.js
const { Pool } = require('pg')
// do other database connection things here
const getPersonById = function (id) {
// use your query here and return the results
}
module.exports = { getPersonById }
Now in your tests, you mock the person module, not the pg module. Imagine if you had 20 some odd tests that all had the mock MySQL pool set up then you changed to pg, you'd have to change all of those, nightmare. But by abstracting your database connection type/setup, it makes testing much easier, because now you just need to stub/mock your person.js module.
const person = require('../person.js') //or whatever relative file path it's in
const sinon = require('sinon')
describe('person.js', function () {
it('is stubbed right now', function () {
const personStub = sinon.stub(person)
personStub.getPersonById.returns('yup')
expect(personStub.getPersonById()).to.eq('yup')
})
})
Below is a simpler approach that means the system-under-test doesn't need any special tricks.
It is comprised of two parts, though the first is "nice to have":
Use a DI framework to inject the pg.Pool. This is a better approach IMO anyway, and fits really well with testing.
In the beforeEach() of the tests, configure the DI framework to use a mock class with sinon.stub instances.
If you aren't using a DI framework, pass the mock as a Pool parameter... but DI is better ;)
The code below is TypeScript using tsyringe, but similar approaches will work fine with plain JavaScript etc.
Somewhere you'll have code that uses pg.Pool. A contrived example:
import { Pool } from 'pg'
...
function getPets(pool: Pool): Promise<Pet[]> {
return pool.connect()
.then(db => db.query(SQL_HERE)
.then(result => {
db.release()
return result.rows // or result.rows.map(something) etc
})
.catch(error => {
db.release()
throw error
})
)
}
That works, and it's fine if you want to pass the Pool instance in. I'd prefer not to, so I use tsyringe like this:
import { container } from 'tsyringe'
...
function getPets(): Promise<Pet[]> {
return container.resolve<Pool>().connect()
.then(...)
}
Exactly the same outcome, but getPets() is cleaner to call - it can be a pain to lug around a Pool instance.
The main of the program would set up an instance in one of a few ways. Here's mine:
...
container.register(Pool, {
useFactory: instanceCachingFactory(() => {
return new Pool(/* any config here */)
})
})
The beauty of this comes out in tests.
The code above (the "system under test") needs a Pool instance, and that instance needs a connect() method that resolves to a class with query() and release() methods.
This is what I used:
class MockPool {
client = {
query: sinon.stub(),
release: sinon.stub()
}
connect () {
return Promise.resolve(this.client)
}
}
Here's the setup of a test using MockPool:
describe('proof', () => {
let mockPool: MockPool
beforeEach(() => {
// Important! See:
// https://github.com/microsoft/tsyringe#clearing-instances
container.clearInstances()
mockPool = new MockPool()
container.registerInstance(Pool, mockPool as unknown as Pool)
})
})
The cast through unknown to Pool is needed because I'm not implementing the whole Pool API, just what I need.
Here's what a test looks like:
it('mocks postgres', async () => {
mockPool.client.query.resolves({
rows: [
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: 'Cat'}
]
})
const r = await getPets()
expect(r).to.deep.equal([
{name: 'Woof', kind: 'Dog'},
{name: 'Meow', kind: Cat'}
])
})
You can easily control what data the mock Postgres Pool returns, or throw errors, etc.

Promisify all methods with Node.js util module

I have a redis util that looks like:
const redis = require('redis')
const { promisify } = require('util')
const client = redis.createClient({
host: '127.0.0.1',
port: '6379'
})
module.exports = {
get: promisify(client.get).bind(client),
hget: promisify(client.hget).bind(client),
set: promisify(client.set).bind(client),
mset: promisify(client.mset).bind(client),
hset: promisify(client.hset).bind(client),
hmset: promisify(client.hmset).bind(client),
...
}
I'd like to rewrite this without all of the repetition. How would I export the client functions with promisify iterated over each method?
Initially, I looked into Object.keys(client) and Object.getOwnPropertyNames(client) as a starting point for grabbing the method names to map over, but neither of these arrays contained them.
Edit: This is closer, is there a better way to phrase this?
const promisifiedClient = {}
for (const fn in Object.getPrototypeOf(client)) {
if (typeof client[fn] === 'function') {
promisifiedClient[fn] = promisify(client[fn]).bind(client)
}
}
module.exports = promisifiedClient
Edit2: Maybe this works (if it's not without some odd side effect of promisifying functions I might not need / want to promisify?)
const redisFunctionList = Object.keys(Object.getPrototypeOf(client))
const promisifiedRedis = redisFunctionList.reduce((acc, functionName) => {
acc[functionName] = promisify(client[functionName]).bind(client)
return acc
}, {})
module.exports = promisifiedRedis
You can use the bluebird package, and then could promisify the redis client simply.
const redis = require("redis");
const bluebird = require("bluebird");
const client = new redis.RedisClient();
/// promise based client of redis
/// storing it in a variable helps with intellisense
const promiseClient = bluebird.promisifyAll(client);
const main = async () => {
/// After that, we just call the default methods with an Async in the end
/// For Example:
/// client.get becomes client.getAsync
/// client.set becaomes client.setAsync
const data = await promiseClient.getAsync("foo");
console.log(data);
const message = await promiseClient.quitAsync();
console.log(message);
};
main();
module.exports = promiseClient;

How to test a function that is part of an object created by a required module in nodejs?

I have this module and the corresponding test with proxyquire.
// sqlQuery.js
const sql = require('mssql');
module.exports = function sqlFn() {
const request = new sql.Request();
}
// sqlQueryTest.js
const proxyquire = require('proxyquire');
const sql = require('mssql');
let sqlStub = sinon.stub(sql);
describe('Sql wrapper', function() {
let sqlQuery = proxyquire('../public/sqlQuery', {
'sql': sqlStub
})
sqlQuery()
it('Should call sql once', function() {
expect(sqlStub.Request.callCount).to.deep.equal(1) // passes
})
})
If in the sqlQuery I want to use the request object's functions, the test won't run:
TypeError: request.query is not a function
I tried rewire as well to set require to a stub:
let stub1 = sinon.stub()
let sqlQuery = rewire('../public/sqlQuery')
sqlQuery.__set__('request', {
input: stub1
})
How can I test if request is called with given methods in the code?

Node.js: how to use proxyquire for replacing neo4j-driver dependency

I must have some fundamental problem understanding of how proxyquire works or doing something wrong.
For a proof of concept I have this original code connecting to neo4j graphnedb in node.js:
// I am lib/neo4j.js
var neo4j = require('neo4j-driver').v1;
var graphenedbURL = process.env.GRAPHENEDB_BOLT_URL;
var graphenedbUser = process.env.GRAPHENEDB_BOLT_USER;
var graphenedbPass = process.env.GRAPHENEDB_BOLT_PASSWORD;
var driver = neo4j.driver(graphenedbURL, neo4j.auth.basic(graphenedbUser, graphenedbPass));
Then I have this test:
// I am test/neo4j.test.js
'use strict';
const test = require('tap').test;
const proxy = require('proxyquire');
const sinon = require('sinon');
test('Testing connection to Neo4j', (assert) => {
const driverStub = sinon.stub();
const testedModule = proxy('../lib/neo4j', {
'neo4j': {
'driver': driverStub,
},
});
});
Test is run as npm tap test/*.test.js --conv
Because npm does not provide access to .env for heroku graphnedb the driver won't have any process.env connection variables which should be ok since my expectation is that proxyquire will replace the driver with above defined stub but that's not happening and the test fails on neo4j.driver missing graphnedebURL. What am I doing wrong please?
You need to proxyquire neo4j the same way you are requiring in the original file, including v1.
// I am test/neo4j.test.js
'use strict';
const test = require('tap').test;
const proxy = require('proxyquire');
const sinon = require('sinon');
test('Testing connection to Neo4j', (assert) => {
const driverStub = sinon.stub();
const testedModule = proxy('../lib/neo4j',
{
'neo4j-driver': {
'v1': {
driver: driverStub
},
},
});
});

Categories

Resources