Test using Nock in a scheduleJob - node.js

I'm getting a problem with test the http request in a scheduleJob node. Well, I've changed the crontime to run every second as suggested here for easier testing. The problem is, I don't think the Nock that I created to test the http request works fine. Because when I do the console.log('res : ', res); it either returns undefined or does not print anything as shown here : enter image description here.
Does anyone have any idea how to solve this?
Index.js
schedule = require('node-schedule'),
var dateObj = Date.now();
dateObj += 1000;
schedule.scheduleJob(new Date(dateObj), function(){
console.log("get's here..");
const url_req = "https://git.ecommchannels.com/api/v4/users";
request.get(
{
url_req,
qs : {private_token: token, per_page: 10}
}, function(err, res, body){
console.log('res : ', res);
var total = res.headers['x-total-pages']
console.log('res.headers', res.headers);
for( i=1; i<=total; i++ ){
url_req2 = "https://git.ecommchannels.com/api/v4/users";
request.get({
url_req2,
qs: {private_token : token, per_page : 10, page : i},
json : true
},
(err2, res2, body2) => {
body2.forEach(element => {
if(condition){
//do something
}
})
})
}
})
});
The unit test:
describe('test the http request', function(){
var clock;
const nockingGit = () =>{
nock('https://git.ecommchannels.com')
.defaultReplyHeaders({
'x-total-pages': 10
})
.get('/api/v4/users')
.query({private_token: 'blabla', per_page: 10})
.reply(201, {});
}
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
clock.restore();
});
it('test the http request', (done)=>{
nockingGit();
setTimeout(function(){
done();
},3000)
clock.tick(3000);
})
})
edited
index.js
schedule.scheduleJob(new Date(dateObj), function(){
var now = new Date();
flag = true;
console.log(now.toLocaleString('en-US', { timeZone: 'Asia/Jakarta'}));
console.log("get's here..");
gitReq();
});
function gitReq(){
const url_req = "https://git.ecommchannels.com/api/v4/users";
request.get(
{
url_req,
qs : {private_token: token, per_page: 10}
}, function(err, res, body){
console.log('res : ', res);
var total = res.headers['x-total-pages']
// console.log('res.headers', res.headers);
for( i=1; i<=total; i++ ){
url_req2 = "https://git.ecommchannels.com/api/v4/users";
request.get({
url_req2,
qs: {private_token : token, per_page : 10, page : i},
json : true
},
// "https://git.ecommchannels.com/api/v4/users?private_token=" + token + "&per_page=10&page=" + i, { json: true },
(err2, res2, body2) => {
body2.forEach(element => {
if(condition){
//do something
}
})
})
}
})
}
Unit Test
*check whether the gitReq() is called - using sinon.spy (but I'm not sure whether the implementation is correct)
const sendReq = {
gitReq: ()=>{}}
describe('test the schedule job', function(){
var clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
});
afterEach(function () {
clock.restore();
});
it('should call the gitReq once', (done)=>{
let gitReq = sinon.spy(sendReq,"gitReq");
sendReq.gitReq();
console.log("callcount : ", gitReq.callCount);
gitReq.restore();
setTimeout(function(){
expect(gitReq.calledOnce).to.be.true;
done();
},3000)
clock.tick(3000);
})})
*test the http request using nock that still returns undefined for the res.
describe('test the http request', function(){
const nockingGit = () =>{
nock('https://git.ecommchannels.com')
.defaultReplyHeaders({
'x-total-pages': 10
})
.get('/api/v4/users')
.query({private_token: 'UKz2cZP9CHz2DT_o2-s9', per_page: 10})
.reply(304, {});
}
it('test the http request', function(){
nockingGit();
_import.gitReq()
})
})
*here's the test result

Related

How can I unit test async database calls without changing the route?

So I have a route with a database call in a separate file:
module.exports = (req, res) => {
const sql = `SELECT Topic FROM surveys WHERE ID="${req.params.id}"`;
model.connection.query(sql, (err, result) => {
if (err) res.status(500).send(err);
res.send(result);
});
};
In my test file, I create fake req and res objects to test different inputs:
const req = {
body: {},
params: {
id: '',
}
};
const res = {
message: '',
stat: 200,
send(arg) {
this.message = arg;
},
status(number) {
this.stat = number;
return this;
}
};
And call it in every test:
it('Should return status 200 if parameter is a number string', () => {
req.params.id = '1';
getTopic(req, res);
chai.expect(res.stat).to.equal(200);
});
My question is, how can I test it asynchronously, without affecting my route?
I've seen Mocha documentation explaining this, but they require a callback in a function.
it('Should return status 200 if parameter is a number string', (done) => {
req.params.id = '1';
getTopic(req, res); // Cannot give any callback
chai.expect(res.stat).to.equal(200);
done(); //Doesn't work
});
I also tried using async/await, but it didn't work (I lack knowledge of how to use it, maybe that's why)
it('Should return status 200 if parameter is a number string', async () => {
req.params.id = '1';
await getTopic(req, res);
chai.expect(res.stat).to.equal(200);
});
And I've seen this question, and didn't help at all: unit testing express route with async callback

Getting error of timeout exceed in mocha

mocha error: timeout of 2000ms exceeded, for async test and hooks ensure the done() callback is being called in this test. if returning a promise, ensure it resolves.
This is what the error i am getting while trying to get response,
This is my index.js file where i export function
exports.info = function(callback) {
var https = require('https');
var options = {
host: 'api.github.com',
path: '/repos/sayanee/build-podcast',
method: 'GET',
headers: { 'User-Agent': 'sayanee' } };
var str = '';
https.request(options, function(response) {
response.on('data', function(data) {
str += data;
});
response.on('end', function() {
callback(JSON.parse(str));
})
response.on('error', function(error) {
console.log(error);
callback();
})
}) .end();
}
This is my indexfile where i describe the test cases
function asyncFunction() {
return new Promise(resolve => {
setTimeout(resolve, 5000);
});
}
describe('Github info', function() {
it.only('returns repo info from github', async function() {
//this.timeout(5000);
await asyncFunction();
word.info(function(reply) {
console.log("er")
expect(reply.language).to.equal('JavaScript');
expect(reply.watchers).to.equal(157);
console.log('RECEIVED');
});
console.log('HELLO'); })
});
Mocha support async test also by passing done callback as param to it that you need to call at test end
describe("Github info", function () {
it.only("returns repo info from github", function (done) {
// set long timeout to be sure word.info finish
this.timeout(5000);
word.info(function (reply) {
console.log("er");
expect(reply.language).to.equal("JavaScript");
expect(reply.watchers).to.equal(157);
console.log("RECEIVED");
// call done at end
done();
});
console.log("HELLO");
});
});
The response is in your question. Mocha is setted up to timeout after 2 second.
Either you makes your request to finish within 2000ms
Either you increase the Mocha timeout, example :
mocha -t 300000
EDIT :
You cannot use async/await mixed up with callbacks
// Wrap your function into a promise
wordInfoPromise() {
return new Promise((resolve, reject) => {
word.info((ret) => {
if (!ret) return reject();
return resolve(ret);
});
});
}
it('returns repo info from github', async function() {
//this.timeout(5000);
await asyncFunction();
const reply = await wordInfoPromise();
console.log("er")
expect(reply.language).to.equal('JavaScript');
expect(reply.watchers).to.equal(157);
console.log('RECEIVED');
console.log('HELLO'); })
});
EDIT 2 :
const req = https.request(options, (res) => {
res.on('data', (d) => {
str += data;
});
res.on('end', () => {
resolve(str);
});
});
req.on('error', (e) => {
reject();
});
req.end();

Mocha with chai and supertest: expected undefined to equal

I wrote the unit tests:
var app = require('../server');
var chai = require('chai');
var supertest = require("supertest")(app);
var GoogleUrl = require('google-url');
var config = require('../config');
var expect = chai.expect;
describe('Urls Tests', function () {
var url = {
author : 'Alexey',
description : 'grrggr',
full_url : 'https://github.com',
date : '30-06-2017',
time : '18:21:27',
count_click : 0,
list_tags : [
'Sport',
'Football'
]
};
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
it('should create a url', function(done) {
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(url.full_url, function (err, shortUrl) {
url.short_url = shortUrl;
supertest
.post('/urls/create')
.send(url)
.expect(401)
.end(function (err, res) {
if (err) return done(err);
expect(res.body.author).to.equal('Alexey');
url = res.body;
done();
});
});
});
it('should modify a url by id', function(done) {
url.description = 'Good description';
url.list_tags.push('Liverpool');
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body.description).to.equal('Good description');
expect(res.body.list_tags[2]).to.equal('Liverpool');
done();
});
});
it('should modify a count of clicks', function(done) {
url.count_click++;
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body).to.equal('Count of the click is updated');
done();
});
});
});
I run to execute the unit tests and get the errors:
I read the articles by unit tests.
First article: http://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/
Second article: https://www.codementor.io/olatundegaruba/integration-testing-supertest-mocha-chai-6zbh6sefz
I don't understand why I get these errors. Please, help me. I think that I made little error, but since I cannot fint it.
UPDATED
I added route:
var express = require('express');
var GoogleUrl = require('google-url');
var _ = require('lodash');
var token = require('../middlewares/token');
var Url = require('../models/url');
var config = require('../config');
var router = express();
router.post('/create', token.required, createShortUrl);
router.put('/count/:id', token.required, updateCountClick);
router.put('/:id', token.required, updateUrlById);
module.exports = router;
function createShortUrl(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
var date = returnDate();
var time = returnTime();
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(req.body.full_url, function (err, shortUrl) {
if (err) {
res.status(500).json(err);
}
var url = new Url({
'author': req.payload.username,
'description': req.body.description,
'full_url': req.body.full_url,
'short_url': shortUrl,
'list_tags': tags,
'date': date,
'time': time
});
url.save(function (err, url) {
if (err) {
return res.status(500).json(err);
} else {
return res.status(200).json(url);
}
});
});
}
function updateCountClick(req, res) {
var count_click = req.body.count_click + 1;
Url.findOneAndUpdate({_id: req.params.id}, {$set: {count_click: count_click}}, {new: true}, function (err, url) {
if (err) {
return res.status(500).json(err);
}
if (url) {
return res.status(200).json('Count of the click is updated');
}
});
}
function updateUrlById(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
Url.findOneAndUpdate({_id: req.params.id}, {$set: {description: req.body.description, list_tags: tags}}, {new: true}, function (err, url) {
if (err) {
res.status(500).json(err);
}
if (url) {
res.status(200).json(url);
}
});
}
UPDATED 2
Authoziration was added:
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
Also I updated code my unit-tests.
I can't see where in your code you send 401 and Url. So it seems that your test requests are getting rejected by token.required middleware with 401 status code (which means "unauthorized").
.send(url)
.expect(401) // why do you expect 401? You never send it inside your logic
So basically your test never hit actual code.
First of all, you do need to fake authorization to make token.required middleware happy.
Then expect 200 result
.send(url)
.expect(200) // normal execution flow of createShortUrl results in 200
.end(/* rest of your test logic */)

Returning wrong URL

I am using phantomejs-node for facebook login . Here is my nodejs code :
var phantom = require('phantom');
phantom.create(function(ph) {
ph.createPage(function(page) {
page.open("https://facebook.com", function(status) {
setTimeout(function () {
page.evaluate((function(URL) {
document.getElementById("email").value = "wrong username";
document.getElementById("pass").value = "wrong password";
document.getElementById("u_0_1").click();
return document.URL;
}), function(result) {
console.log('Page url is ' + result);
ph.exit();
}, 5000);
});
});
//page.render("page2.png");
});
});
Instead of returning https://www.facebook.com/login.php?login_attempt=1 , its returning https://www.facebook.com/ . By the way here is Phantomjs code that I am following :
var page = require('webpage').create();
page.open("http://www.facebook.com/login.php", function(status) {
if (status === "success") {
page.evaluate(function() {
document.getElementById("email").value = "#gmail.com";
document.getElementById("pass").value = "";
document.getElementById("u_0_1").click();
});
window.setTimeout(function() {
var url = page.evaluate(
function () {
return document.URL;
}
);
console.log( "- current url is " + url );
page.render("page.png");
phantom.exit();
}, 5000);
}
});
Try this code:
var phantom = require('phantom');
phantom.create(function(ph) {
ph.createPage(function(page) {
page.open("https://facebook.com", function(status) {
page.evaluate((function() {
document.getElementById("email").value = "#gmail.com";
document.getElementById("pass").value = "password";
document.getElementById("login_form").submit();
return;
}), function() {
console.log("loaded");
setTimeout(function(){
page.evaluate(function () {
return document.URL;
},function(result){
page.render("page2.png",function(){
console.log("done rendering");
});
console.log("Page url is "+result);
ph.exit();
});
},6000)
});
});
});
});
Hope this is helpfull :)
If you're tired of callback hell you could also give phridge a try. I've written this bridge because I didn't want to wrap all assignments and function calls with callbacks. It stringifies the given function and runs it inside PhantomJS.
A-0-'s solution would look like:
var page;
// creates a new PhantomJS process
phridge.spawn()
.then(function (phantom) {
return phantom.openPage("https://facebook.com");
})
.then(function (p) {
page = p;
return page.run(function (resolve) {
// this function runs inside PhantomJS
var page = this;
page.evaluate(function () {
document.getElementById("email").value = "#gmail.com";
document.getElementById("pass").value = "password";
document.getElementById("login_form").submit();
});
setTimeout(function () {
page.render("page2.png");
resolve();
}, 6000);
});
})
.then(function () {
// page2.png rendered
});

Testing functions that contains async calls

I'm trying to test the get function:
exports.get = function(req, res) {
Subscriptions
.find(req.params.id)
.success(function(subscription) {
if (subscription) {
res.json({message: "Success"}, 200);
} else {
res.json({message: "Not found"}, 404);
}
})
.error(function(error) {
res.json({message: "Internal server error"}, 500);
});
};
Specifically, I don't really care if it hits the database, I only want to test the scenarios where the success and error events occur. I'm using sequelize.js as my orm to handle the database. I've gotten a test up and running, but its a bit nasty, with the timeout. Is there a better way of doing this? Here's the test I've written so far:
var express = require('express')
, sinon = require('sinon')
, subscription = require('app/controllers/subscriptions')
, Subscriptions = require('app/models/subscriptions')
;
describe('subscription controller', function() {
beforeEach(function() {
this.mockResponse = sinon.mock(express.response);
});
afterEach(function() {
this.mockResponse.restore();
});
describe('GET /subscriptions/:id', function() {
it('should return a json response', function(done) {
var request = {
params: {
id: 'identifier'
}
};
var expectedResponse = {
subscriptions_uri : "/subscription/identifier"
};
this.mockResponse
.expects('json')
.once()
.withArgs(expectedResponse);
subscription.get(request, express.response);
setTimeout(function() {
done();
}, 500);
});
});
});
I decided to use the supertest library, which made testing my controller incredibly easy:
var express = require('express')
, subscription = require('app/controllers/subscriptions')
, request = require('supertest')
, app = express()
;
describe('subscription controller', function() {
describe('GET /subscriptions/:id', function() {
it('should return a json response', function(done) {
var expectedBody = {
subscriptions_uri : "/subscription/identifier"
};
request(app)
.get('/subscriptions/identifier')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(expectedBody)
.expect(200, done);
});
});
});

Resources