The db object comes from the cloudant module.
This is the code I'm trying to test:
res.set('some-header', 'some-value');
res.status(200);
db.attachment.get('some-uuid', 'file').
on('error', function(e) {
console.log('err');
reject(e);
}).
pipe(base64.decode()).pipe(_res).on('error', function(e) {
console.log('err2');
reject(e);
});
fulfill(null);
Trying to mock the code above:
var sinon = require('sinon');
var request = require('supertest');
var attachment_1 = {
get: sinon.stub(),
};
var db = {
attachment: attachment_1
};
var obj = {};
var obj2 = {};
var sample = {};
obj.pipe = function(encoderfunction) {
console.log('obj.pipe for encoding');
return obj2;
};
obj2.pipe = function(res) {
console.log('obj2.pipe');
console.log(typeof res); // object
return this;
};
obj2.on = function() {
console.log('obj2');
};
sample.on = function() {
console.log('sample.on');
return obj;
};
db.attachment.get.withArgs('some-uuid', 'file').returns(sample);
This is the actual test:
it('should respond with file contents and status of 200', function(done) {
request(app).get('/file/index.html')
.expect(200)
.end(function(err, res){
if (err) {
console.log(err);
}
console.log('res: ' + res);
done();
});
});
But I keep getting this error:
1) should respond with file contents and status of 200:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
From the supertest docs:
If you are using the .end() method .expect() assertions that fail will
not throw - they will return the assertion as an error to the .end()
callback. In order to fail the test case, you will need to rethrow or
pass err to done(), as follows:
describe('GET /users', function() {
it('respond with json', function(done) {
request(app)
.get('/users')
.set('Accept', 'application/json')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
Related
I'm working with net in node.js and I'm sending packet to a server and listening to the response, but I can't manage to return it. Here's my code:
function packetsend (sockeT, packeT) {
var resp = null;
if(sockeT) {
sockeT.write(packeT);
sockeT.on('data', (data) => {
resp = data.toString()
})
}
return resp;
}
const socket = new net.Socket();
socket.connect({host: server, port: port}, function() {
console.log('Connected');
var packetRecv = packetsend(socket, 'some packet');
if (packetRecv === 'some') {
console.log("ok");
}
})
I don't understand why packetsend() function is not returning the updated resp variable, and sends undefined object instead. When I do console.log(data) inside the sockeT.on() I see that it receives data.
Try transforming your packetsend() function in an async function. Maybe it's not returning the resp because you return it before the event 'data' is invoked.
This is just an example of a possible implementation:
function packetsend (sockeT, packeT) {
return new Promise((resolve, reject) => {
if (sockeT) {
sockeT.write(packeT);
sockeT.on('data', (data) => {
resolve(data.toString());
});
//WARNING: I don't know this 'socketT' object, don't know if there is an 'error' event.
//but it's recommended to handle errors. This is just an example.
sockeT.on('error', (error) => {
reject(error);
});
}
else{
reject('Missing sockeT');
}
});
}
const socket = new net.Socket();
socket.connect({ host: server, port: port }, function () {
console.log('Connected');
packetsend(socket, 'some packet').then(packetRecv => {
console.log('Received data => '+ packetRecv);
if (packetRecv === 'some') {
console.log("ok");
}
}).catch(error => console.log(error));
})
Update: you can also use the async/await
const socket = new net.Socket();
socket.connect({ host: server, port: port }, async function () {
try {
console.log('Connected');
let packetRecv = await packetsend(socket, 'some packet');
console.log('Received data => '+ packetRecv);
if (packetRecv === 'some') {
console.log("ok");
}
}
catch (error) {
console.log(error);
}
});
Tips:
"promise" documentation
"eventEmitter" documentation
"async/await" documentation
I am trying to unit test my controller method and this is what it returns when I try to stub it : TypeError: Attempted to wrap findById which is already wrapped. I have tried using the before and after hooks but it does not fix the issue. I am using sinon-express-mock for req and res and sinon for stubs.
describe('controller getUserData', function() {
beforeEach(function() {
sinon.stub(UserData, "findById");
});
afterEach(function() {
UserData.findById.restore();
});
it('getUserData by Id', async function() {
const mockId = sinon.stub(UserData, 'findById').returns(mockIdData[0]);
req = mockReq();
res = mockRes();
next = null;
req.params.id = mockIdData[0]._id;
await Controller.getUserData(req, res, next);
assert(mockId.calledWith(req.params.id));
assert(res.status.calledWith(200));
});
it('should return 404 when user data is not found', async function() {
sinon.stub(UserData, 'findById').returns(null);
req = mockReq();
res = mockRes();
next = null;
await Controller.getUserData(req, res, next);
assert(res.status.calledWith(404));
});
});
Here is my controller method:
exports. getUserData = async (req, res, next) => {
const userData = await UserData.findById(req.params.id);
if (!userData) {
res.status(404).send();
}
res.status(200)
.json(
userData,
);
};
First Node/Express app.
I'm having a hard time wrapping my head around on how to retrieve data from an endpoint and rendering it in the browser.
I have a dataservice.js that gets a JSON object from an endpoint like this:
const http = require('http');
getFinhockeyData = function() {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
console.log(`Got response: ${res.statusCode}`);
var body = "";
res.on('data', function (chunk) {
body += chunk;
})
res.on('end', function () {
var data = JSON.parse(body);
console.log('data parsed.');
console.log('first team name: ' + data.teams[0].TeamName);
console.log(typeof data);
return data;
})
}).on('error', (e) => {
console.log(`Got error from Finhockey: ${e.message}`);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Up until now things work and the data object can be console.logged and its content is usable.
The router.js looks currently like this:
'use strict';
const express = require('express');
const async = require('async');
const router = express.Router();
const dataservice = require('./dataservice.js')
router.get('/', function(req, res) {
async.series([
function(callback) {
getFinhockeyData(callback)
}
],
function(err, results) {
console.log('start rendering');
res.render('index', { data: data });
})
});
module.exports = router;
When I run the app and refresh the / route, I can see from the console that the getFinhockeyData is called and the data object's content is available in dataservice.js's console.logs, but the browser window hangs and the res.render part is never reached.
I understand that the rendering should be done only after the endpoint data request has finished (async.series usage), but it seems that I lack a fundamental understanding on how to actually use the result data from the getFinhockeyData function in the main route.
Any advice on this? I'll be happy to provide more info if necessary.
Firstly, doing the request is asynchronous, so you'll have to use either a callback or a promise.
Even the async middleware won't let you just return data from an asynchronous call, it requires a callback, but using native promises seems easier here
const http = require('http');
getFinhockeyData = function() {
return new Promise( (resolve, reject) => {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
var body = "";
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
resolve( JSON.parse(body) );
});
}).on('error', reject);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Also note that you're exporting as a module with a property
module.exports.getFinhockeyData = getFinhockeyData;
when you're going to use that in the routes, you have to use the property
const dataservice = require('./dataservice.js');
router.get('/', function(req, res) {
dataservice.getFinhockeyData().then(function(data) {
res.render('index', { data: JSON.stringify(data) });
}).catch(function(err) {
// epic fail, handle error here
});
});
You are responding to your route call with
res.render('index', { data: data });
But there is no data variable. It should be
res.render('index', { data: results });
Which is the variable where you are storing your data when it comes from the callback
The reason for res.render() not being called is, http requests are async. To get the response a callback must be passed, which you did but forgot to call it in the dataservice.js
This should help...
Change your dataservice.js like the following...
const http = require('http');
getFinhockeyData = function(callback) {
http.get('http://tilastopalvelu.fi/ih/modules/mod_standings/helper/standings.php?statgroupid=3545', (res) => {
console.log(`Got response: ${res.statusCode}`);
var body = "";
res.on('data', function (chunk) {
body += chunk;
})
res.on('end', function () {
var data = JSON.parse(body);
console.log('data parsed.');
console.log('first team name: ' + data.teams[0].TeamName);
console.log(typeof data);
callback(null, data); //returning the data to the callback
})
}).on('error', (e) => {
console.log(`Got error from Finhockey: ${e.message}`);
callback(e, null);
});
}
module.exports.getFinhockeyData = getFinhockeyData;
Change your router.js like the following...
router.get('/', function(req, res) {
async.series([
function(callback) {
getFinhockeyData(callback)
}
],
function(err, results) {
if(err === null){
console.log('start rendering');
res.render('index', { data: results[0] });
}
})
});
I am having db.js with db related functions, I want to make call to db.js and wait until it returns the query result.
But the result is returned after the execution of the db call. Can anyone please help how to solve this.
Code sample:
var Q = require('q');
db= require("./dbaccess.js");
function waitfor(ms){
var deferred = Q.defer();
setTimeout(function() {
deferred.resolve(db);
}, 5000);
return deferred.promise;
}
waitfor(2000).done(function(dbcall) {
console.log('contrived example '+ dbcall.query1());
});
dbacess.js:
var sql = require('mssql');
var config = {
user: 'xx',
password: 'xxx',
server: 'aaa',
database: 'RequestCenter',
stream: true,
}
this.query1=function() {
sql.connect(config, function(err) {
var result;
var request = new sql.Request();
request.query("select * from dbo.AcAccount where Name like 'AutomationCli%' ");
request.on('row', function(row) {
console.log(row.Name);
result = row.Name;
});
request.on('error', function(err) {
console.log("err : "+err);
});
request.on('done', function(returnValue) {
console.log("done");
});
return result;
});
sql.on('error', function(err) {
console.log("sql err : "+err);
});
}
Output:
contrived example undefined
in db: AutomationClient
Expected output:
in db: AutomationClient
contrived example AutomationClient
Not sure why your main code passes in 2000 for the ms argument and then does a 5000ms timeout, in fact, why are you doing a timeout at all, if that was some attempt to wait for the db function to complete, then you don't need it
If you must use promises - personally I'd use a simple callback for such simple code, however, I get that you want to learn how to use Promises
Your original code looked like it was attempting to return the value of the LAST row.name
This code returns an array of row.name
Not knowing the type of data you'd be getting, I don't know which is correct
dbacess.js
var Q = require('q');
var sql = require('mssql');
var config = {
user: 'xx',
password: 'xxx',
server: 'aaa',
database: 'RequestCenter',
stream: true,
}
this.query1 = function() {
var deferred = Q.defer();
sql.connect(config, function(err) {
var result = []; // return all rows - modify as required
var request = new sql.Request();
request.query("select * from dbo.AcAccount where Name like 'AutomationCli%' ");
request.on('row', function(row) {
console.log(row.Name);
result.push(row.Name);
});
request.on('error', function(err) {
console.log("err : " + err);
deferred.reject(err);
});
request.on('done', function(returnValue) {
deferred.resolve(result);
});
});
sql.on('error', function(err) {
console.log("sql err : " + err);
deferred.reject(err);
});
return deferred.promise;
}
Code sample:
db = require("./dbaccess.js");
db.query1().then(function(result) {
console.log('contrived example ' + result);
});
I'm starting an integration test suite for my Node.js app. I'm currently trying to write a setup script that wipes the test database and populates it with some test data. I'm trying to avoid the dreaded "Pyramid of Doom", and I was hoping to use promises as a way of preventing my code from getting out of hand. I'm very new to promises, and I'm still trying to wrap my head around them - it's possible I'm not using them correctly.
Here is my initial version without promises. This works, but has nested callbacks up the wazoo:
var mongoose = require('mongoose');
var User = require('./user');
var MONGODB_URL = process.env.MONGODB_TEST_URL || 'localhost:27017/swot_test';
console.log('\nRunning e2e test preparation script');
console.log('-----------------------------------\n');
console.log('Connecting to database:', MONGODB_URL, '...')
mongoose.connect(MONGODB_URL, function () {
console.log('Wiping database...')
mongoose.connection.db.dropDatabase(function () {
console.log('Setting up test user...')
User.createUser({
email: 'test#example.com',
password: 'tester'
}, function (err, user) {
if (err) throw err;
// If there was more setup, it would have to go here... pyramid of doom!
console.log('Finished.');
process.exit();
});
});
});
Here is a version that uses Q promises:
var Q = require('q');
var mongoose = require('mongoose');
var User = require('./user');
var MONGODB_URL = process.env.MONGODB_TEST_URL || 'localhost:27017/swot_test';
console.log('\nRunning e2e test preparation script');
console.log('-----------------------------------\n');
Q.fcall(function () {
var deferred = Q.defer();
console.log('Connecting to database:', MONGODB_URL, '...');
mongoose.connect(MONGODB_URL, function (err) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Wiping database...');
mongoose.connection.db.dropDatabase(function (err) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Setting up test user...');
User.createUser({
email: 'test#example.com',
password: 'tester'
}, function (err, user) {
if (err) deferred.reject(new Error(err));
else deferred.resolve();
});
return deferred.promise;
})
.done(function () {
console.log('Finished.');
process.exit();
}, function (err) {
console.error('An error occurred:', err.stack);
});
I like that it has less nesting, but there's a lot of repetition in there. Is there a way I could use the helper functions in the Q API to make this code more concise and less repetitive? Especially this part:
if (err) deferred.reject(new Error(err));
else deferred.resolve();
I would appreciate any help with cleaning up this code.
Q.ninvoke(mongoose,'connect', MONGODB_URL)
.then(function () {
console.log('Wiping database...');
return Q.ninvoke(mongoose.connection.db, 'dropDatabase');
})
.then(function () {
console.log('Setting up test user...')
return Q.ninvoke(User, 'createUser', {
email: 'test#example.com',
password: 'tester'
});
})
.then(function (user) {
console.log('Finished.');
process.exit();
})
.catch(function(err) {
console.log(err);
});
You can possibly shorten it with reusable oncomplete helper like this:
function oncomplete(deferred) {
return function (err, result) {
if (err) deferred.reject(new Error(err));
else deferred.resolve(result);
}
}
Q.fcall(function () {
var deferred = Q.defer();
console.log('Connecting to database:', MONGODB_URL, '...');
mongoose.connect(MONGODB_URL, oncomplete(deferred));
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Wiping database...');
mongoose.connection.db.dropDatabase(oncomplete(deferred));
return deferred.promise;
})
.then(function () {
var deferred = Q.defer();
console.log('Setting up test user...');
User.createUser({
email: 'test#example.com',
password: 'tester'
}, oncomplete(deferred));
return deferred.promise;
})
.done(function () {
console.log('Finished.');
process.exit();
}, function (err) {
console.error('An error occurred:', err.stack);
});
If you're brave enough, you can drastically simplify it with Node v0.11 beta and yield keyword. That would implement an asynchronous state machine, so you could have pseudo-liner code flow without explicit callbacks.
Check out the async module, specifically waterfall. You can read about it here:
https://github.com/caolan/async#waterfalltasks-callback
Basically it allows you to chain a set of nested callbacks in a nice and concise manner.