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

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
},
},
});
});

Related

proxyquire Error: ENOENT: no such file or directory, scandir

Hello I am new to testing with mocha/chai/sinon and sequelize-test-helpers
Trying to use proxyquire to override the require but having issues
Getting this following error about the path:
Error: ENOENT: no such file or directory, scandir 'C:<local-directories-path>\ecommerce-pern-app\server\src\models'
I dont get why there is a src folder when I don't have a src folder at all I am using the proxyquire in the test file and its path is from the server directory would be:
server/specs/services/user-service.spec.js
"use strict";
const chai = require('chai');
const {match, stub, resetHistory, spy} = require('sinon');
const proxyquire = require('proxyquire');
const path = require('path');
const service = path.resolve('./services/userService.js')
var sinonChai = require("sinon-chai");
chai.should();
chai.use(sinonChai);
console.log(service)
const {makeMockModels, sequelize, dataTypes,} = require('sequelize-test-helpers');
describe('Idea Controller', function () {
const uid = '6a88e9b5-33a2-403f-ac3d-e86413ac101d'
const data = {
email: 'testface#test.com',
password: '123456',
is_admin: false,
first_name: 'Testy',
last_name: 'McTestface',
google_id: null,
facebook_id: null
}
describe('findAll()', function () {
it('Success case ', function () {
const mockResponse = () => {
const res = {};
res.json = stub().returns(res);
return res;
};
let res = mockResponse();
const User = {findAll: stub()};
const mockModels = makeMockModels({User});
Idea.findAll.resolves(data);
const UserService = proxyquire(service, {
"save": {}
});
UserService.findAll({}, res);
Idea.findAll.should.have.been.called; // passes
res.json.should.have.been.called; //fails
});
})
});
In the above code I am using the proxyquire like this:
const proxyquire = require('proxyquire');
const path = require('path');
const service = path.resolve('./services/userService.js')
const {makeMockModel} = require('sequelize-test-helpers');
const mockModels = makeMockModels({User});
const UserService = proxyquire(service, {
"../models": mockModels
});
As I am trying to use the path to find the server/service/userService.js file which is relatively located from the test file at ../../services/userService.js. I have got this bug that there is src folder there when I do not have a src directory at all even!
As the bug is saying:
Error: ENOENT: no such file or directory, scandir 'C:<local-directories-path>\ecommerce-pern-app\server\src\models'
Whatever I try about file path is not working I tried path.resolve, path.join and directly typing the path into it as like ../../services/userService.js
here is the
server/services/userService.js
const Models = require('../models');
const { User } = Models;
const save = async ({ id, ...data }) => {
const user = await User.findOne({ where: { uid: id } })
if (user) return await user.update(data)
return null
}
module.exports = save;
I just want the path to with proxyquire to work
What is this \src\models path from the error, I dont have a src/models path route at all!
This is a quote from sequelize-test-helpers's readme.
As a convenience, makeMockModels will automatically populate your mockModels with mocks of all of the models defined in your src/models folder (or if you have a .sequelizerc file it will look for the models-path in that). Simply override any of the specific models you need to do stuff with.
So you need to provide .sequelizerc file and define models-path.

How to stub a libary function in JavaScript

For example, if I have main.js calling a defined in src/lib/a.js, and function a calls node-uuid.v1, how can I stub node-uuid.v1 when testing main.js?
main.js
const a = require("./src/lib/a").a
const main = () => {
return a()
}
module.exports = main
src/lib/a.js
const generateUUID = require("node-uuid").v1
const a = () => {
let temp = generateUUID()
return temp
}
module.exports = {
a
}
tests/main-test.js
const assert = require("assert")
const main = require("../main")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
assert.equal(main(), "121321")
})
})
The sinon.stub(...) statement doesn't stub uuid.v1 for src/lib/a.js as the above test fails.
Is there a way to globally a library function so that it does the specified behavior whenever it gets called?
You should configure the stub before importing the main module. In this way the module will call the stub instead of the original function.
const assert = require("assert")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
const main = require("../main")
assert.equal(main(), "121321")
})
})
Bear in mind that node-uuid is deprecated as you can see by this warning
[Deprecation warning: The use of require('uuid') is deprecated and
will not be supported after version 3.x of this module. Instead, use
require('uuid/[v1|v3|v4|v5]') as shown in the examples below.]
About how to stub that for testing would be a bit more harder than before as actually there is no an easy way to mock a standalone function using sinon
Creating a custom module
//custom uuid
module.exports.v1 = require('uuid/v1');
Requiring uuid from the custom module in your project
const uuid = require('<path_to_custom_module>');
Sinon.stub(uuid, 'v1').returns('12345');

Stub method of instance

I have an Express app that uses node-slack-sdk to make posts to Slack when certain endpoints are hit. I am trying to write integration tests for a route that, among many other things, calls a method from that library.
I would like to prevent all default behavior of certain methods from the Slack library, and simply assert that the methods were called with certain arguments.
I have attempted to simplify the problem. How can I stub a method (which is actually nested within chat) of an instance of an WebClient, prevent the original functionality, and make assertions about what arguments it was called with?
I've tried a lot of things that haven't worked, so I'm editing this and providing a vastly simplified set-up here:
index.html:
const express = require('express');
const {WebClient} = require('#slack/client');
const app = express();
const web = new WebClient('token');
app.post('/', (req, res) => {
web.chat.postMessage({
text: 'Hello world!',
token: '123'
})
.then(() => {
res.json({});
})
.catch(err => {
res.sendStatus(500);
});
});
module.exports = app;
index.test.html
'use strict';
const app = require('../index');
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
const expect = chai.expect;
chai.use(chaiHttp);
const {WebClient} = require('#slack/client');
describe('POST /', function() {
before(function() {
// replace WebClient with a simplified implementation, or replace the whole module.
});
it('should call chat.update with specific arguments', function() {
return chai.request(app).post('/').send({})
.then(function(res) {
expect(res).to.have.status(200);
// assert that web.chat.postMessage was called with {message: 'Hello world!'}, etc
});
});
});
There are a few things that make this difficult and unlike other examples. One, we don't have access to the web instance in the tests, so we can't stub the methods directly. Two, the method is buried within the chat property, web.chat.postMessage, which is also unlike other examples I've seen in sinon, proxyquire, etc documentation.
The design of your example is not very testable which is why you're having these issues. In order to make it more testable and cohesive, it's better to pass in your WebClient object and other dependencies, rather than create them in your route.
const express = require('express');
const {WebClient} = require('#slack/client');
const app = express();//you should be passing this in as well. But for the sake of this example i'll leave it
module.exports = function(webClient) {
app.post('/', (req, res) => {
web.chat.postMessage({
text: 'Hello world!',
token: '123'
})
.then(() => {
res.json({});
})
.catch(err => {
res.sendStatus(500);
});
})
return app;
};
In order to implement this, build your objects/routes at a higher module. (You might have to edit what express generated for you. I'm not sure, personally I work with a heavily refactored version of express to fit my needs.) By passing in your WebClient you can now create a stub for your test.
'use strict';
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
const expect = chai.expect;
chai.use(chaiHttp);
const {WebClient} = require('#slack/client');
const web = new WebClient('token');
let app = require('../index')(web);
describe('POST /', function() {
it('should call chat.update with specific arguments', function() {
const spy = sinon.spy();
sinon.stub(web.chat, 'postMessage').callsFake(spy);
return chai.request(app).post('/').send({})
.then(function(res) {
expect(res).to.have.status(200);
assert(spy.calledWith({message: 'Hello world!'}));
});
});
});
This is known as Dependency Injection. Instead of having your index module build it's dependency, WebClient, your higher modules will pass in the dependency in order for the them to control the state of it's lower modules. Your higher module, your test, now has the control it needs to create a stub for the lower module, index.
The code above was just quick work. I haven't tested to see if it works, but it should answer your question.
So #Plee, has some good points in term of structuring. But my answer is more about the issue at hand, how to make the test work and things you need to understand. For getting better at writing unit tests you should use other good resources like books and articles, I assume there would be plenty of great resources online for the same
The first thing you do wrong in your tests is the first line itself
const app = require('../index');
Doing this, you load the index file which then executes the below code
const {WebClient} = require('#slack/client');
const app = express();
const web = new WebClient('token');
So now the module has loaded the original #slack/client and created an object which is not accessible outside the module. So we have lost our chance of customizing/spying/stubbing the module.
So the first thumb rule
Never load such modules globally in the test. Or otherwise never load them before stubbing
So next we want is that in our test, we should load the origin client library which we want to stub
'use strict';
const {WebClient} = require('#slack/client');
const sinon = require('sinon');
Now since we have no way of getting the created object in index.js, we need to capture the object when it gets created. This can be done like below
var current_client = null;
class MyWebClient extends WebClient {
constructor(token, options) {
super(token, options);
current_client = this;
}
}
require('#slack/client').WebClient = MyWebClient;
So now what we do is that original WebClient is replaced by our MyWebClient and when anyone creates an object of the same, we just capture that in current_client. This assumes that only one object will be created from the modules we load.
Next is to update our before method to stub the web.chat.postMessage method. So we update our before method like below
before(function() {
current_client = null;
app = require('../index');
var stub = sinon.stub();
stub.resolves({});
current_client.chat.postMessage = stub;
});
And now comes the testing function, which we update like below
it('should call chat.update with specific arguments', function() {
return chai.request(app).post('/').send({})
.then(function(res) {
expect(res).to.have.status(200);
expect(current_client.chat.postMessage
.getCall(0).args[0]).to.deep.equal({
text: 'Hello world!',
token: '123'
});
});
});
and the results are positive
Below is the complete index.test.js I used, your index.js was unchanged
'use strict';
const {WebClient} = require('#slack/client');
const sinon = require('sinon');
var current_client = null;
class MyWebClient extends WebClient {
constructor(token, options) {
super(token, options);
current_client = this;
}
}
require('#slack/client').WebClient = MyWebClient;
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
chai.use(chaiHttp);
let app = null;
describe('POST /', function() {
before(function() {
current_client = null;
app = require('../index');
var stub = sinon.stub();
stub.resolves({});
current_client.chat.postMessage = stub;
});
it('should call chat.update with specific arguments', function() {
return chai.request(app).post('/').send({})
.then(function(res) {
expect(res).to.have.status(200);
expect(current_client.chat.postMessage
.getCall(0).args[0]).to.deep.equal({
text: 'Hello world!',
token: '123'
});
});
});
});
Based on the other comments, it seems like you are in a codebase where making a drastic refactor would be difficult. So here is how I would test without making any changes to your index.js.
I'm using the rewire library here to get and stub out the web variable from the index file.
'use strict';
const rewire = require('rewire');
const app = rewire('../index');
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
const expect = chai.expect;
chai.use(chaiHttp);
const web = app.__get__('web');
describe('POST /', function() {
beforeEach(function() {
this.sandbox = sinon.sandbox.create();
this.sandbox.stub(web.chat);
});
afterEach(function() {
this.sandbox.restore();
});
it('should call chat.update with specific arguments', function() {
return chai.request(app).post('/').send({})
.then(function(res) {
expect(res).to.have.status(200);
const called = web.chat.postMessage.calledWith({message: 'Hello world!'});
expect(called).to.be.true;
});
});
});

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?

Stubbing Redis interactions in javascript using Sinon

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);
},
}));

Categories

Resources