How to mock a 'request' node module for testing in jest - node.js

I am new to writing test cases in jest and i wanted to test 'mark' function and want to mock 'request' node module. let's say this file's name is app.js and test file will be app.test.js
Can someone tell how to write its test case?
const request = require("request")
var test = {
mark: function(data,cb){
data.url = "localhost"
request(data, function(err,response,body){
if(!response){
err.response = false
}
cb(err,body)
})
}
}
module.exports = test;

If I understand your question correctly, you are asking two questions:
How to write the test and mock the request
What test cases you should write
Regarding the mock, I recommend using nock which is an npm module for mocking network requests.
To the second question, I believe that you should map the logic in the function and create the test cases from there. Think about the function, look at every if else/calculation/loop and it’s possible outcomes and create test cases from there.
The mark function doesn’t have a lot of logic, it’s send a request, updates the err variable according to the response and calling the callback.
The only outcome we can test to to see that if the request works, the cb is called correctly without modifications. And if the request returns empty, change the err var and call the callback.
To do so we need to mock the request and return a response to match the test case and validate that the cb was called correctly (can be done with a mock).
Test case example:
The test case may be a bit abstract since I don’t have the real use case of the function but it shows the point
it("Should mark response false when it does not exist", () => {
const DATA = {} // your data object
const callback = jest.fn();
nock('localhost')
.get('/example')
.reply(500) // Exaple of mocking the resonse. Custom it to be more specific depending on mark.
test.mark(DATA, callback);
// Verify that the function was called.
expect(callback).toHaveBeenCalled();
// Verify that `err.response` was false.
expect(callback.mock.calls[0][0].response).toBe(false)
})
You can read more about mocking and verifying parameters here.

Related

Is there any way to mock a variable inside a function?

The code works like this
let getToken = await apiCall();
apiCall() calls a few internal functions and populates getToken variable with a token. The problem is, in my tests, I can't seem to populate that variable with anything. It is always undefined and an error gets thrown. Is there any way to populate this variable with a dummy value in a unit test?
EDIT
constructor() {
this._token = '';
}
I want to inject a value in token via a unit test. I'm trying to use sinon right now.

Unit testing mongoose validation with promise resolve/reject

I'm struggling to find a way to unit test the validation of a field in mongoose. I currently have the following test:
it('should be invalid if googleId is empty', () =>
testBook.googleId = '';
const newBook = new Book(testBook);
return newBook.validate().catch((err) => {
should.exist(err.errors.googleId);
});
});
This test is not working as intended because, if I have a correct validation for googleId, the test will pass because it will assert the err.errors.googleId exists, but if I don't have any validation for googleId, it will also pass because the .catch() never gets called and it assumes the test is passing.
I've tried putting should.fail() inside a .then(), but that way it always ends up in .catch() but with different errors, if there's no validation it will be catched with the should.fail() error, if there's validation it will catch with the model validation error.
What would be the best way to achieve this kind of testing?
Thanks alot!
EDIT:
I've tried using the callback of newBook.validate() and it works, but I never use callbacks for model methods, I always tend to use promises, so should I be using the callback in this particular case or there's still a better way to handle it using promises?
You can use the second parameter to then and put something that throws in the first for example:
return newBook.validate().then(
resp => throw new Error("Test failed this should not resolve"),
err => should.exist(err.errors.googleId);
});
So now if the newBook.validate() is not rejected the test will fail. Since you aren't using catch() you don't stop it from causing the test to fail like it should. If the promise is rejected like it should be your test passes (assuming it meets the conditions set). This works because unlike catch() the error callback of then() doesn't catch errors thrown in then(). Of course you can put anything in the first callback that throws an error like assert.fail() or whatever your library has.
There is a chai-as-promised plugin that includes helpers to make Promise testing work more like normal chai expect/should does.
.rejectedWith and .rejected handle errors largely like regular .throw.
const chai = require('chai')
chai.should()
chai.use(require('chai-as-promised'))
it('should be invalid if googleId is empty', function(){
testBook.googleId = ''
const newBook = new Book(testBook)
return newBook.validate()
.should.be.rejectedWith('Validation Failed')
.and.eventually.have.nested.property('errors.googleId')
});
Just make sure you load the plugin last if you are loading any other chai plugins otherwise things go haywire.

How to test socket.io events being called with sinon.spy

First of all, I'm trying to test the second time a function being called returns the correct value. To be more specific, I'm trying to test the second time an event is received by socket.on() returns the correct value.
I know that Sinon.spy() can detect whether a function being called or not. But it seems not working with socket.io events.
I'm trying to test for example,
var socketio = io.connect(someUrl);
socketio.on(eventName, cb);
if the 'eventName' is called. I tried
var spy = sinon.spy(socketio, 'on');
assert(spy.withArgs(eventName).called);
But it says it's never being called.
Furthermore, what if I'd like to test the data in the callback function is right or not, for example
var socketio = io.connect(someUrl);
socketio.on(eventName, function(data) {
data.should.equal(something);
});
Is it possible to test that?
Update:
I solved the problem by adding a count in callback
var count = 0;
socketio.on(eventName, function(data) {
count++;
if(count === 2) {
data.should.equal(something)
}
}
But I think this is a hacky way, is there any standard/smart way to do it(if possible using sinon)?
You don't want to spy on on -- that's only called once, at the time you set up the listener. (Unless you actually want to spy to see if the listeners are being set up at all, and don't care if they're actually used. That doesn't appear to be what you want here.)
Instead, you want to spy on the cb callback function, to see if and how it runs.
var callbackSpy = sinon.spy(cb);
Then, pass that spy in as the callback:
socketio.on(eventName, callbackSpy);
And test if the callback was ever run with the desired argument value:
assert(callbackSpy.calledWith(something));

How can I Mock node-rest-client request?

I'm using node-rest-client for my project and I've come to the point where I want to start unit testing my classes that utilize node-rest-client. Is there any examples on how I can mock the Client in my tests? I am using Sinon in my unit testing.
Step 1: In the js code you are testing, export the node-rest-client instance so it can be available to your test code. For example in myApp.js, I put this:
var Client = require('node-rest-client').Client;
var restClient = new Client();
exports.restClient = restClient; //instance now available outside this module
restClient.get(someUrl, ...); //actual node rest client call
Step 2: In your test code, create a wrapper function that returns a fake function, and use sinon to mock it into your target code. This allows you to inject the return data and response code during your test setup.
var nodeRestGet = function (data, statusCode) {
return function (url, cb) {
cb(data, { statusCode: statusCode || 200 });
}
};
sinon.stub(myApp.restClient, 'get').callsFake(nodeRestGet("", 200));
Step 3: Write your test code. Note that you might want to restore the method (remove the mock) if you are running multiple tests:
myApp.doThings(); // TEST
myApp.restClient.get.restore(); // removes the mock

When testing an ExpressJS route using mocha+sinon, how do you "stub" a function that's local to the route?

So in one file, I have this route defined:
router.post('/', security.authenticate, function(req, res, next) {
//Doing prep stuff
//Do database work (<< this is what im really testing for)
//Calling another work function.
createFormUser(req.body, (ret) => {
return res.json(ret.createdUser);
});
});
followed by this function:
var createFormUser = (ourUser, call) => {
// does a bunch of misc work and creation for another database
// unrelated to current tests.
}
I want to to test this route. Normally, I would just create a sandbox instance of the database so that it can do whatever it wants, make an http request to the route in the test, and finally do expects() in the return from that http call.
However, I don't want the "createFormUser" function to be called, because 1) it does some fancy shit that's really hard to contain for this test 2) I will be testing it elsewhere.
In a normal test I would at this point use sinon to stub the function. But in this case I don't actually have an object reference, since this is all done through HTTP requests to server that mocha spools up when testing.
So my question is the same as the title, how can stub/replace/ignore/etc this method so it doesn't get called during the test?
As stated by #DavidKnipe, all I had to do was export the methods via:
module.exports.createFormUser = (ourUser, call) => { ... }
And was able to both test the method individually and prevent it's execution via a sinon.stub.

Resources