I'm using tape and I'm trying to test a couple of functions. My functions throw errors and validate objects. I like throwing errors because then later my promises can catch them. I'm trying to run simple tests and establishing the data argument in all the scenarios to hit each error in the stack. How can I test this function without putting it in a try / catch every time? I see there's two functions in the API t.throws() and t.doesNotThrow(), I've tried them both and even added the extra params like t.throws(myFunc({}), Error, "no data") but nothing seems to work as expected.
var test = require('tape')
var _ = require('underscore')
function myFunction(data){
if(!data) throw new Error("no data")
if(_.size(data) == 0) throw new Error("data is empty")
if(!data.date) throw new Error("no data date")
if(!data.messages.length == 0) throw new Error("no messages")
data.cake = "is a lie"
return data
}
test("my function", function(t){
t.throws(myFunction({}))
t.end()
}
I have no loyalty to tape, and I have no clue what I'm doing. I just want to simply test functions synchronous that throw exceptions, without a ton of overhead. So if there's a better unit-testing framework for this use case I'd be glad to use it. If tape has this ability I'd love to use it.
Is this how it should be done?
test("my function", function(t){
try{
myFunction({})
t.fail()
}catch(e){
t.pass(e.message)
}
t.end()
})
Seems I cant call the function within the argument of t.throws because it throws the error, duh. I believe this is the correct usage.
t.throws(function(){
myFunction({})
})
Instead of bind as suggested in the comment above, u could use the arrow function like this:
t.throws(() => myFunction({}), 'should throw an exception')
Related
can I throw error and use await word in one statement using AND operator, the following code explains well my request :
throw new Error() && await client.end().
This latter works fine till now, and for my question, is this the better form of writing this and will it create problems in some cases.
My main purpose is to close the Data Base connection and throw the Error.
for such use cases I think the finally block is the right solution. See on the mozilla docs here. In your case it would be something like
try {
// stuff that can throw an error
} catch {
// handle the error case
} finally {
// in any case, close the connection
await client.end()
}
Or, if you want to keep the connection open when there's no error, just move the close line in the catch block:
try {
// stuff that can throw an error
} catch {
// handle the error case
await client.end()
}
I'm still learning the language, and I'm very curious to know what is the proper way to ensure that either all functions will execute or none when one action requires series of functions to be executed. For example I might have an HTML button that calls some apply() function:
function apply() {
try {
// Check arguments, choose what exactly to do next through some IFs etc...
}
anotherFunction();
}
function anotherFunction() {
try {
// Request data from DB, process data received, update object variables, etc...
}
yetAnotherFunction();
}
function yetAnotherFunction() {
try {
// Update HTML
}
oneMoreFunction();
}
function oneMoreFunction() {
try {
// Update graph
}
}
So the problem here is that if any of the functions in the flow throws an error the rest functions won't do what they should, hence the entire Apply process will be interrupted with some changes applied (let's say HTML is getting updated) but the rest (the graph) is not. I'm curious to know what is the best practice to prevent this behaviour? Yes I'm trying my best to use try {} and check arguments for errors etc, but it looks like I can't foresee everything, I just need some way to tell the code "ensure you can execute all of the functions, in case of any errors, just don't do anything at all". Please advise what can be done here?
You're taking the right path when thinking about try/catch blocks, but notice I used a 'catch' as well. Usually (maybe that is even enforced, I can't remember) you need the catch blocks along with the try.
So your functions could look something like this:
function async myFirstTryCatch() {
try {
// Make your request in the try block
await requestCall();
} catch(error){
// Hey, my http call returned an error
// Deal with the error here. Maybe show a toast, validate a form
// Anything you need to not break the code and have good UX
console.log(error)
}
}
In that same line of thought, you could have each function handle their own try/catch or maybe control that in your apply function, in case some of the chain must continue/stop depending each other.
function apply() {
try {
firstCall();
functionThatRequiresFirstCalltoSucceed();
} catch (error){
//Will catch if either firstCall or functionThatRequiresFirstCalltoSucceed fail
console.log(error)
}
functionThatIndependsFromTheResultAbove();
}
I hope this will help you build your thoughts about error handling in JS :)
IMPORTANT NOTE
If your code enters the catch block, it will consider that the error has been dealt with and will not propagate! Here's an example
function functionThatThrowsError(){
try{
throw new Error('Example Error!');
} catch (error) {
// Error has been dealt with
console.log(error) // Returns "Example Error"
// throw error; <--- Throw the error in the catch block if you need t to propagate
}
}
function wontCatchError() {
try {
functionThatThrowsError();
} catch (error) {
// THE CODE WILL NOT ENTER THE CATCH BLOCK
// SINCE THE ERROR WAS CAUGHT IN THE FUNCTION ITSELF.
// If you need to catch here as well, make sure to throw the error
// in the catch block of the 'functionThatThrowsError'
console.log(error)
}
}
Background
I have a REST API using MongoDB, Node.js and Express that makes a request to my NoSQL DB and depending on different results, I want to differentiate which error I send the customer.
Problem
The current version of my code has a generic error handler and always sends the same error message to the client:
api.post("/Surveys/", (req, res) => {
const surveyJSON = req.body;
const sender = replyFactory(res);
Survey.findOne({_id: surveyJSON.id})
.then(doc => {
if(doc !== null)
throw {reason: "ObjectRepeated"};
//do stuff
return new Survey(surveyJSON).save();
})
.then(() => sender.replySuccess("Object saved with success!"))
.catch(error => {
/*
* Here I don't know if:
* 1. The object is repeated
* 2. There was an error while saving (eg. validation failed)
* 3. Server had a hiccup (500)
*/
sender.replyBadRequest(error);
});
});
This is a problem, because the client will always get the same error message, no matter what and I need error differentiation!
Research
I found a possible solution, based on the division of logic and error/response handling:
Handling multiple catches in promise chain
However, I don't understand a few things:
I don't see how, at least in my example, I can separate the logic from the response. The response will depend on the logic after all!
I would like to avoid error sub-classing and hierarchy. First because I don't use bluebird, and I can't subclass the error class the answer suggests, and second because I don't want my code with a billion different error classes with brittle hierarchies that will change in the future.
My idea, that I don't really like either
With this structure, if I want error differentiation, the only thing I can do is to detect an error occurred, build an object with that information and then throw it:
.then(doc => {
if(doc === null)
throw {reason: "ObjectNotFound"};
//do stuff
return doc.save();
})
.catch(error => {
if(error.reason === "ObjectNotFound")
sendJsonResponse(res, 404, err);
else if(error.reason === "Something else ")
sendJsonResponse(/*you get the idea*/);
else //if we don't know the reasons, its because the server likely crashed
sendJsonResponse(res, 500, err);
});
I personally don't find this solution particularly attractive because it means I will have a huge if then else chain of statements in my catch block.
Also, as mentioned in the previous post, generic error handlers are usually frowned upon (and for a good reason imo).
Questions
How can I improve this code?
Objectives
When I started this thread, I had two objectives in mind:
Having error differentiation
Avoid an if then else of doom in a generic catcher
I have now come up with two radically distinct solutions, which I now post here, for future reference.
Solution 1: Generic error handler with Errors Object
This solution is based on the solution from #Marc Rohloff, however, instead of having an array of functions and looping through each one, I have an object with all the errors.
This approach is better because it is faster, and removes the need for the if validation, meaning you actually do less logic:
const errorHandlers = {
ObjectRepeated: function(error){
return { code: 400, error };
},
SomethingElse: function(error){
return { code: 499, error };
}
};
Survey.findOne({
_id: "bananasId"
})
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists."};
//saving empty object for demonstration purposes
return new Survey({}).save();
})
.then(() => console.log("Object saved with success!"))
.catch(error => {
respondToError(error);
});
const respondToError = error => {
const errorObj = errorHandlers[error.reason](error);
if (errorObj !== undefined)
console.log(`Failed with ${errorObj.code} and reason ${error.reason}: ${JSON.stringify(errorObj)}`);
else
//send default error Obj, server 500
console.log(`Generic fail message ${JSON.stringify(error)}`);
};
This solution achieves:
Partial error differentiation (I will explain why)
Avoids an if then else of doom.
This solution only has partial error differentiation. The reason for this is because you can only differentiate errors that you specifically build, via the throw {reaon: "reasonHere", error: "errorHere"} mechanism.
In this example, you would be able to know if the document already exists, but if there is an error saving the said document (lets say, a validation one) then it would be treated as "Generic" error and thrown as a 500.
To achieve full error differentiation with this, you would have to use the nested Promise anti pattern like the following:
.then(doc => {
//we dont want to add this object if we already have it
if (doc !== null)
throw { reason: "ObjectRepeated", error:"Object could not be inserted because it already exists." };
//saving empty object for demonstration purposes
return new Survey({}).save()
.then(() => {console.log("great success!");})
.catch(error => {throw {reason: "SomethingElse", error}});
})
It would work... But I see it as a best practice to avoid anti-patterns.
Solution 2: Using ECMA6 Generators via co.
This solution uses Generators via the library co. Meant to replace Promises in near future with a syntax similar to async/await this new feature allows you to write asynchronous code that reads like synchronous (well, almost).
To use it, you first need to install co, or something similar like ogen. I pretty much prefer co, so that is what i will be using here instead.
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
The generator function requestHandler will yield all Promises to the library, which will resolve them and either return or throw accordingly.
Using this strategy, you effectively code like you were coding synchronous code (except for the use of yield).
I personally prefer this strategy because:
Your code is easy to read and it looks synchronous (while still have the advantages of asynchronous code).
You do not have to build and throw error objects every where, you can simply send the message immediately.
And, you can BREAK the code flow via return. This is not possible in a promise chain, as in those you have to force a throw (many times a meaningless one) and catch it to stop executing.
The generator function will only be executed once passed into the library co, which then returns a Promise, stating if the execution was successful or not.
This solution achieves:
error differentiation
avoids if then else hell and generalized catchers (although you will use try/catch in your code, and you still have access to a generalized catcher if you need one).
Using generators is, in my opinion, more flexible and makes for easier to read code. Not all cases are cases for generator usage (like mpj suggests in the video) but in this specific case, I believe it to be the best option.
Conclusion
Solution 1: good classical approach to the problem, but has issues inherent to promise chaining. You can overcome some of them by nesting promises, but that is an anti pattern and defeats their purpose.
Solution 2: more versatile, but requires a library and knowledge on how generators work. Furthermore, different libraries will have different behaviors, so you should be aware of that.
I think a good improvement would be creating an error utility method that takes the error message as a parameter, then does all your ifs to try to parse the error (logic that does have to happen somewhere) and returns a formatted error.
function errorFormatter(errMsg) {
var formattedErr = {
responseCode: 500,
msg: 'Internal Server Error'
};
switch (true) {
case errMsg.includes('ObjectNotFound'):
formattedErr.responseCode = 404;
formattedErr.msg = 'Resource not found';
break;
}
return formattedErr;
}
Try catch is used to catch errors and report to user. all said and fine. But where exactly one has to put the try-catch. Or What exactly has to go inside try catch on a usual basis.
Most importantly, is it absolutely a good coding practice to have a try catch block?
I think it is good practce to use a try catch if it will handle errors and prevent the program from crashing.
Taken from W3 Schools:
The try statement lets you test a block of code for errors.
The catch statement lets you handle the error.
The throw statement lets you create custom errors.
The finally statement lets you execute
code, after try and catch, regardless of the result.
An example:
fuction foo()
{
try
{
// Block of code to try
}
catch(e)
{
// Block of code to handle errors
document.getElementById("demo").innerHTML = e.message;
}
finally
{
// Block of code to be executed regardless of the try / catch result
}
}
Here is soem more documentation at W3 Schools: http://www.w3schools.com/js/js_errors.asp
Yes, it absolutely good practice to use try-catch blocks. Here's a rather simplistic (but contrived) demonstration.
function safeParseJSON(json) {
try {
return JSON.parse(json);
} catch(exception) {
// could not parse this JSON
console.error(exception);
} finally {
return null;
}
}
Parsing JSON is the most common scenario I have encountered for using the try-catch construct, hence the example.
However, the usual try-catch mechanism doesn't work when the function is asynchronous. It's essential to understand this if you are ever using a server side Javascript platform.
The standard pattern for async event handling is as follows.
db.connect(options, function connected(err, client) {
if(err) throw err;
client.query(...);
});
Because that callback function is run somewhere else (presumably when the database connects) we can't wrap it with a try-catch block. Instead, most async method calls will pass the error as the first argument to the callback.
This way we can handle the error as and when it happens. If there is no error, null will be passed as the first argument, so that it can be ignored.
A number of implementations of promises try to recreate this mechanism, but in an asynchronous way.
Here's an example with Q:
db.connect(options)
.then(function(client) {
client.query(...);
})
.catch(function (error) {
throw error;
})
.fin(function() {
// finally
db.close();
});
You can also use Q on the client side, if you are working with asynchronous functions.
Does anyone know if there a way to disable error handling by Mocha?
I'd really like to let the exception bubble up and be handled by the browser during debugging.
Update:
The context is this.
- Some code throw's an unexpected error.
- I would like to disable "try/catch" (something like jasmine's tests do) and be thrown into the debugger in chrome.
This way I can inspect the current state of the application at that moment.
It sounds like there isn't something built in, and I will need to manually do this each time I get some odd error in a test. Maybe by wrapping my code with
try {
somethingThatBreaks();
} catch(e) {
debugger;
}
Mocha installs its own onerror handler to trap exceptions that may occur in asynchronous code. You can disable it with:
window.onerror = undefined;
This is dangerous. You should do this only when you must absolutely do so. If you make this a routine thing, Mocha will completely miss errors that happen in asynchronous code.
If what you want to do is let exceptions in synchronous code bubble up, I'm not seeing what this would accomplish. If you want to inspect the exception before Mocha touches it, a try...catch block wrapping your test will do this.
Mocha's error handling is contained in the following code:
if (this.async) {
try {
this.fn.call(ctx, function(err){
if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
done();
});
} catch (err) {
done(err);
}
return;
}
// sync
try {
if (!this.pending) this.fn.call(ctx);
this.duration = new Date - start;
fn();
} catch (err) {
fn(err);
}
You could possibly throw the error with the catch (or remove the try..catch entirely) however it would most likely break the test suite if a test fails.
mocha.options.allowUncaught=true
But in this case, the rest of the tests will break if the error is not caught within mocha. Disabling try{} catch(){} in mocha will only be useful when debugging.
To create a test, I suggest using the construction
(for browser)
try{
throw new Error()
} catch (e){
// Let's say it's called in a yor error listeners.
e.handler=()=>{
done(e)
}
let event= new ErrorEvent('error',{error:e})
window.dispatchEvent(event);
}