Mocking Redis Constructor with Sinon - javascript

I'm trying to figure out a way to mock redis in this module:
const Redis = require('ioredis');
const myFunction = {
exists: (thingToCheck) {
let redis_client = new Redis(
6379,
process.env.REDIS_URL,
{
connectTimeout: 75,
dropBufferSupport: true,
retryStrategy: functionHere
});
redis_client.exists(thingToCheck, function (err, resp) {
// handlings in here
});
}
};
Using this test-code:
const LambdaTester = require('lambda-tester');
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const mockRedis = sinon.mock(require('ioredis'));
describe( 'A Redis Connection error', function() {
before(() => {
mockRedis.expects('constructor').returns({
exists: (sha, callback) => {
callback('error!', null);
}
});
});
it( 'It returns a database error', function() {
return LambdaTester(lambdaToTest)
.event(thingToCheck)
.expectError((err) => {
expect(err.message).to.equal('Database error');
});
});
});
I also tried a few variations, but I'm kind of stuck as I basically need to mock the constructor and I'm not sure Sinon supports this?
mockRedis.expects('exists').returns(
(thing, callback) => {
callback('error!', null);
}
);
sinon.stub(mockRedis, 'constructor').callsFake(() => console.log('test!'));
sinon.stub(mockRedis, 'exists').callsFake(() => console.log('test!'));
Not sure what else to try here, I also tried using rewire as suggested here, but using mockRedis.__set__("exists", myMock); never set that private variable.
I want to fake my error paths ultimately.
I would love to hear what others are doing to test redis in node js 😄.

Your problem is not whether Sinon supports this or that, but rather a missing understanding of how "classes" work in Ecmascript, as shown by the attempt at stubbing constructor property in the test code. That will never work, as that property has nothing to do with how any resulting objects turn out. It is simply a reference to the function that was used to create the object. I have covered a very similar topic on the Sinon tracker that you might have interest in reading to gain some core JS foo :-) Basically, it is not possible to stub a constructor, but you can probably coerce your code to use another constructor function in its place through either DI or link seams.
As a matter of fact, a few answers down in the same thread, you will see me covering an example of how I myself designed a Redis using class to be easily testable by supporting dependency injection. You might want to check it out, as it is more or less directly applicable to your example module above.
Another technique, which you already has tried getting to work, is using link seams (using rewire). The Sinon homepage has a nice article on doing this. Both rewire and proxyquire will do the job just fine here: I think you have just complicated the matter a bit by wrapping the require statement with a mock.
Even though I am on the Sinon maintainer team, I never use the mock functionality, so I cannot tell you how to use that, as I think it obscures the testing, but to get the basic link seams working using rewire I would basically remove all the Sinon code first and get the basic case going (removing redis for a stubbed module you have created).
Only then, add Sinon code as required.

Related

Repository pattern practical use cases and implementation in node.js

Can someone please explain what’s the use of this pattern with example?
All I'm confused is that I can have database instance wherever I want and I have flexibility to do anything by it, am I wrong? specially is
The repository pattern is a strategy for abstracting data access.
it’s like putting a universal adapter in between your application and your data so it doesn’t matter what data storage technology you use. All your app wants is having defined operations on items, it shouldn’t have to care about how it’s stored or where it comes from.
Also, there's no need to mention that all impacts of changes will be handled from one place instead of cascading all through your code!
Personally, I love this design pattern because it allows me to only have concerns about my business logics at the first steps instead of dealing with variant databases, on top of that, it solves a huge amount of my headaches when it comes to writing tests! So instead of stubbing or spying databases, which can be a headache, I can simply enjoy a mock version of operations
Now let’s implement a sample in js, it can be just as simple as below code (it's a simplified sample of course)
// userRepository.js
const userDb = [];
module.exports = {
insert: async (user) => userDb.push(user),
findAll: async () => userDb,
};
here is how I use this pattern, first I write something like below code in a 5 minute
// userRepository.js
const userDb = new Map();
module.exports = Object.freeze({
findById: async (id) => userDb.get(id),
insert: async (user) => userDb.set(user.id, user),
findAll: async () => Array.from(userDb.values()),
removeById: async (id) => userDb.delete(id),
update: async (updatedUser) => {
if (!userDb.has(updatedUser.id)) throw new Error('user not found');
userDb.set(updatedUser.id, { ...userDb.get(updatedUser.id), ...updatedUser });
},
});
Then I start to write my unit tests for repository that I’ve just written and business use-cases and so on…
anytime I’m satisfied with everything I can simply use a real database, because it’s just an IO mechanism, isn’t it? :)
So in above code I’ll replace userDb with a real database and write real data access methods, and of course expect all my tests to be passed.

Trouble stubbing getter method in firebase-admin with sinon

I'm trying to develop unit tests for cloud functions. Some of them call firebase-admin.messaging().sendToDevice() to deliver notifications that I don't want to actually send during testing.
My reference in Firebase docs
Sinon Stubs Reference
I'd like to know why Object.defineProperty is working but stub.get() (the commented out section) does not. I have a concern it might not get restored using sinon.restore(). By not work I mean it uses the real methods and sends a notification. Thank you.
it('should return resolved promise for success', function(done) {
const num = 99;
const messagingStub = sinon.stub();
const sendStub = sinon.stub();
Object.defineProperty(admin, 'messaging', { get: () => messagingStub });
// sinon.stub(admin, 'messaging').get(function getterFn() {
// return messagingStub;
// });
messagingStub.returns({sendToDevice: sendStub});
sendStub.returns(Promise.resolve({successCount: num}));

Mocking Request Header module using Jest

function createRequest(method) {
const init = {
method,
headers: new Headers({.....}),
};
return new Request(url, init); }
I am using Request headers (with Fetch) in the above code (https://davidwalsh.name/fetch )
However while writing unit test cases using Jest, it gives me this error: ReferenceError: Headers is not defined
DO I need to mock even these standard modules? How should I import Headers in unit test cases
I say yes, mocking Headers is definitely an option in a testing context.
In my particular case, I have simply mocked it like so:
global.Headers = ()=>{}
This will work just fine if you want to test that your code is behaving properly based on the response returned by fetch.
If you also need to check that the correct headers are sent, you would need a more sophisticated mock, and/or perhaps a specialized test suite for your networking methods.
adding ' import "isomorphic-fetch" ' to the jest setup file should resolve this issue as this will make the missing dom APIs available in the tests
I know this is an old question, but for anyone who runs into this (as I did), this worked for me:
// before running each test
beforeEach(() => {
// define `append` as a mocked fn
const append = jest.fn();
// set test `Headers`
global.Headers = () => ({
append: append,
});
});

node jasmine - how to unit test on function that involve redis call?

I just started playing around Jasmine and I'm still struggling on the spyon/mocking things, e.g., I have a function
module.exports = (() => {
....
function getUserInfo(id) {
return new Promise((resolve, reject) => {
redis.getAsync(id).then(result => {
resolve(result)
})
})
}
return { getUserInfo: getUserInfo }
})()
Then I start writing the Jasmine spec
describe('Test user helper', () => {
let userInfo
beforeEach(done => {
userHelper.getUserInfo('userid123')
.then(info => {
userInfo = info
done()
})
})
it('return user info if user is found', () => {
expect(userInfo).toEqual('info of userid 123')
})
})
It runs well, but my question is how can I mock the redis.getAsync call, so it can become a real isolated unit test?
Thanks.
Good question. You can mock out the redis dependency but only if you rewrite you code, slightly, to be more testable.
Here, that means making redis a parameter to the factory that returns the object containing getUserInfo.
Of course, this changes the API, callers now need to call the export to get the object. To fix this, we can create a wrapper module that calls the function with the standard redis object, and returns the result. Then we move the actual factory into an inner module, which still allows it to be tested.
Here is what that might well look like
user-helper/factory.js
module.exports = redis => {
....
function getUserInfo(id) {
return redis.getAsync(id); // note simplified as new Promise was not needed
}
return {getUserInfo};
};
user-helper/index.js
// this is the wrapper that preserves existing API
module.exports = require('./factory')(redis);
And now for the test
const userHelperFactory = require('./user-helper/factory');
function createMockRedis() {
const users = [
{userId: 'userid123'},
// etc.
];
return {
getAsync: function (id) {
// Note: I do not know off hand what redis returns, or if it throws,
// if there is no matching record - adjust this to match.
return Promise.resolve(users.find(user => user.userId === id));
}
};
}
describe('Test user helper', () => {
const mockRedis = createMockRedis();
const userHelper = userHelperFactory(mockRedis);
let userInfo;
beforeEach(async () => {
userInfo = await userHelper.getUserInfo('userid123');
});
it('must return user info when a matching user exists', () => {
expect(userInfo).toEqual('info of userid 123');
});
});
NOTE: As discussed in comments, this was just my incidental approach to the situation at hand. There are plenty of other setups and conventions you can use but the primary idea was just based on the existing export of the result of an IIFE, which is a solid pattern, and I leveraged the NodeJS /index convention to preserve the existing API. You could also use one file and export via both module.exports = factory(redis) and module.exports.factory = factory, but that would, I believe, be less idiomatic in NodeJS. The broader point was that being able to mock for tests, and testability in general is just about parameterization.
Parameterization is wonderfully powerful, and its simplicity is why developers working in functional languages sometimes laugh at OOP programmers, such as yours truly, and our clandestine incantations like "Oh glorious Dependency Injection Container, bequeath unto me an instanceof X" :)
It is not that OOP or DI get it wrong it is that testability, DI, IOC, etc. are just about parameterization.
Interestingly, if we were loading redis as a module, and if we were using a configurable module loader, such as SystemJS, we could do this by simply using loader configuration at the test level. Even Webpack lets you do this to some extent, but for NodeJS you would need to monkey patch the Require Function, or create a bunch of fake packages, which are not good options.
To the OP's specific response
Thanks! That's a good idea, but practically, it seems it's quite strange when I have tons of file to test in which I will need to create a factory and index.js for each of them.
You would need to restructure your API surface and simply export factories that consuming code must call, rather than the result of applying those factories, to reduce the burden, but there are tradeoffs and default instances are helpful to consumers.

Module returning asynchronously initialited object

I'm having this "design" problem that's driving me crazy.
My goal is having a decoupled RabbitMQ client. It has to be able to init it's connection and "return" a created channel so my publishing module can use it.
Code looks like this (i know that is not the better code but i expect it serves for this explanation).
var createConnection = (function() {
var channel;
var connect = function(){
// amqp connect
// error handling
createChannel();
});
}
var createChannel = function(){
//amqpConn.createConfirmChannel...
}
//pseudo
return{
getChannel : function(){
if(!initiated)
connect();
return channel;
}
}
})();
module.exports = createConnection;
Now, important things:
1- I know this ain't gonna work and i know why, its a simplification.
2- I'm aware that i can accomplish my goals by using async or promises.
3- Maybe has no sense decoupling a rabbit client but is for understanding purposes
That said, my questions:
1- Is there any way i can accomplish this without using other modules?
2- if so, can be accomplished in a fancy and stylish way?
3- Is there any fancy solution that allows 3rd party code executing a simple "publish(exchange, channel, msg)" been sure that connection has been established?
I feel able to work with JS but sometimes you just need do things one way only to know that you can but this is giving me some headache.
Truly thanks and i hope the question was understood :)
One way I've found to handle this is to wrap your asynchronous object in an object that is aware of the asynchronous state of your object and presents the same API regardless of whether or not the asynchronous object has finished initializing.
For example, you could wrap your channel object in another object that presents the same methods but internally checks if the actual channel object is initialized. If it is, use it as normal. If it isn't, wait for it to be initialized and use it as normal. The user of the wrapper object wouldn't need to know if the channel is actually initialized. The main drawback of this is every wrapper method that needs to access channel must be asynchronous even if the method it's accessing on channel is synchronous.
Example:
function initializeChannel() {
return new Promise((resolve, reject) => {
// create, initialize, and resolve channel
});
}
module.exports = { // wrapper
channelPromise: initializeChannel(),
foo(a) {
return this.channelPromise.then((channel) => channel.foo(a));
}
};

Categories