I am writing an updated testing library for Node.js and am trying to properly trap errors that occur in test callbacks
for some reason, the following code doesn't trap an AssertionError:
process.on('uncaughtException',function(err){
console.error(err); //an instance of AssertionError will show up here
});
[file1,file2,file2].forEach(function (file) {
self.it('[test] ' + path.basename(file), {
parallel:true
},function testCallback(done) {
var jsonDataForEnrichment = require(file);
request({
url: serverEndpoint,
json: true,
body: jsonDataForEnrichment,
method: 'POST'
}, function (error, response, body) {
if (error) {
done(error);
}
else {
assert(response.statusCode == 201, "Error: Response Code"); //this throws an error, which is OK of course
done();
}
});
});
});
I handle the callback (I named it "testCallback" above), with this code:
try {
if (!inDebugMode) {
var err = new Error('timed out - ' + test.cb);
var timer = setTimeout(function () {
test.timedOut = true;
cb(err);
}, 5000);
}
test.cb.apply({
data: test.data,
desc: test.desc,
testId: test.testId
}, [function (err) { //this anonymous function is passed as the done functon
cb(err);
}]);
}
catch (err) { //assertion error is not caught here
console.log(err.stack);
cb(err);
}
I assume the problem is that callbacks that result from async functions like those made in the request module, cannot be trapped by simple error handling.
What is the best way to trap that error?
Should I just flesh out the process.on('uncaughtException') handler? Or is there a better way?
The best way to handle this appears to be Node.js domains, a core module
https://nodejs.org/api/domain.html
it will likely be deprecated soon, but hopefully there will be a replacement that can have similar functionality, because the domain module is saving my ass right now, as I have no other way to trap errors, because the errors might be generated by my users' code, not my code.
Related
I can create 4 error scenarios when calling http.get(url, cb):
httpThrows()
Can be triggered with a wrong format of the url, or wrong callback, etc.
function httpThrows() {
try {
http.get("www.missing-protocol.com", res => {
console.log(res.statusCode);
});
} catch (error) {
console.log("http.get() throws an error.");
}
}
requestError()
It the main error handler and triggers on some network related issue, e.g. DNS lookup failed or server not responding, etc.
function requestError() {
var req = http.get("http://some-url-that-does-not-exist.com", res => {
console.log(res.statusCode);
});
req.on("error", err => {
console.log("req.on('error') called with error");
});
}
errorCode()
Server responded normally so no network errors (can handle server errors).
function errorCode() {
http.get("http://httpstat.us/501", res => {
console.log("Got error code:", res.statusCode);
});
}
responseError() (the problem)
An http.IncomingMessage is given in the callback as response or res. According to the documentation it is a Readable steam and that steam can emit an error event.
function responseError() {
http.get("http://some-ulr-with-error-halfway-through.com/", res => {
console.log(res.statusCode);
// This will never be emitted?
res.on("error", err => {
console.log("res.on('error') called with error", err);
});
});
}
So this last handler:
Is this event ever triggered when using http.request or http.get?
If so what can trigger the event?
For my understanding the only way to end up with an error in that case is if there would be an issue with Node or the Engine and in both cases you can't do much about it.
In this situations I prefer not to handle those cases because you have less code to review and maintain.
I want to update a field in the 'NotesDB' that indicates the number of comments on a specific Note. Parse Cloudcode should do this automatically after saving a comment.
In practice it sometimes does and it sometimes doesn't (even with the same user, on the same note). The comment itself is always saved properly.
Is there any way i can improve this code..?
Parse.Cloud.afterSave("CommentsDB", function(request) {
var OriginalNote = request.object.get("OriginalPostId");
var query = new Parse.Query("NoteDB");
query.get(OriginalNote, {
success: function(post) {
post.increment("NumberOfComments");
post.save();
},
error: function(error) {
console.log("An error occured :(");
}
});
As you code stands, it is not possible to see if the post.save() call is failing or not (and if it is, why), maybe try chaining your promises :
query.get(OriginalNote,{useMasterKey:true}).then (function (post) {
post.increment("NumberOfComments");
return post.save(null,{useMasterKey:true});
}).then (function (savedPost) {
console.log('post incremented ok to ' + savedPost.get('NumberOfComments'));
},function (err) {
//this function will catch any error in the promise chain : query.get() or post.save()
console.error('An error occured : ' + err.message);
});
We're using knex.js to generate and execute SQL. We're having trouble trapping errors when adding fields to an existing table and the documentation does not cover this specific use-case. Attempting to add a duplicate field does return an error in the console (VSCODE), but none of standard events are called on an error so we can't trap it in code. Here's the code:
knex.schema.table(tableName, function(table) {
table.string('test').catch(function(e) {
callback(e)
});
}).then(function(e) {
callback(e);
}).catch(function(e) {
callback(e);
})
This is returned in the VSCODE console:
{ [TypeError: table.string(...).catch is not a function] 'error#context': { accessToken: undefined } }
However, none of the callbacks are called. How can we check for errors when adding fields?
UPDATE #1 This code will call the callback, but obviously without any error information. And, the same error appears in the console regardless:
table.string('test').catch(
callback(null, Lib.returnEvent(false))
);
UPDATE #2 No callbacks are called with the following code:
knex.schema.table(tableName, function(table) {
table.string('ddd');
}).then(function(e) {
callback(e);
}).catch(function(e) {
callback(e);
})
UPDATE #3 In this example the first callback is called, but the function hangs on subsequent calls:
knex.schema.table(tableName, function(table) {
table.string('ddd');
callback(true);
}).then(function(e) {
callback(true, e);
}).catch(function(e) {
callback(false, e);
})
I was able to resolve this issue by switching to the Knex callback approach rather than using the promises approach. This code works consistently:
knex.schema.table(tableName, function(table) {
table.string(tableFieldname);
}).asCallback(function(err) {
if (err) {
callback(false, err);
} else {
callback(true);
}
})
This works for me
return knex('users')
.insert({
username: req.body.username,
password: hash,
})
.returning('*')
.bind(console)
.then(console.log)
.catch(console.error);
We're using the Q promise library in our node API, but allow functions to be called via callbacks.
For example:
function foo(cb) {
Q.fcall(function () {
return true;
}).then(function (result) {
return cb(null, result);
}).catch(function (err) {
return cb(err, null);
});
}
When I run my mocha unit tests, if there is an exception in the callback, it results in the the callback being called twice.
For example:
var called = 0;
foo(function (err, res) {
called++;
console.log('err: ' + err);
console.log('res: ' + res);
console.log('called: ' + called);
throw Error(throw Error from foo!');
});
This gives the following result:
err: null
res: true
called: 1
err: Error: throw Error from foo!
res: null
called: 2
One approach we found was to do the following:
function foo2(cb) {
var _result, _err;
Q.fcall(function () {
return true;
}).then(function (result) {
_result = result;
}).catch(function (err) {
_err = err;
}).finally(function () {
_.defer(cb, _err, _result);
});
}
The idea was to have one place where the callback would be called and try to prevent developer errors by enforcing the defer to clear the call stack.
With this approach, our callback is called once, and any errors (or in our case, asserts) get handled directly by the caller.
Is there a better technique we can use? I feel like this is a pretty common scenario, so I'd imagine there exists a cleaner solution...
Modify your foo function to handle both the fulfillment and the rejection in the same then call using 2 separate handlers:
function foo(cb) {
Q.fcall(function () {
return true;
}).then(function (result) {
return cb(null, result);
}, function (err) {
return cb(err, null);
});
}
I'm new to Promises in JavaScript, and whilst it seems to be working for me to an extent, I'm unable to test the 'reject' value.
I'm passing through an Error, and want to ensure that it is an error and more importantly, that the error code matches what I'm expecting.
return new Promise(function(resolve, reject){
tableService.deleteEntity(config.azureTable.tableName,
visitor.azureEntity(), function (error, response) {
// If successful, go on.
if (!error) {
resolve(response);
}
// If unsuccessful, log error.
else {
/* If we know it's a resourceNotFound
that's causing the error, return that. */
if (error.code === 'ResourceNotFound') {
reject(new Error('Record not found'));
}
// For unexpected errros.
else {
reject(new Error('Table service error (delete): ' + error));
}
}
});
});
The test, in Mocha - using chai and chai-as-promised. Everything else is working (I have 24 passing tests) - but this one has me stuck!
it('return an error when the lookup fails', function (done) {
storage.delete(globalUUID).then(function(sucess) {
done(sucess);
}, function(error) {
expect(error).to.be.an(Error);
done();
});
});
Any help would be greatly appreciated.
You are not using chai-as-promised anywhere. If your first code example is the body of the storage.delete method, then your test should look like:
it('return an error when the lookup fails', function() {
expect(storage.delete(globalUUID)).to.be.rejectedWith(Error);
});