The package description for dirty-chai is a bit opaque for a newbie:
Function form for terminating assertion properties
What is an 'assertion property' in this context and what does terminating mean? Why are they not normally "terminated"? I have so many questions. An example without dirty-chai and and an example with, that shows the benefit would be great.
I think one of the key reasons for dirty-chai is that it is not considered good practice to make assertions on property access (at least for some in the JavaScript community).
Without the plug-in chai allows you write tests like this:
chai.expect(someFunctionThatReturnsTrue(args)).to.be.true
If the test failed, that means someFunctionThatReturnsTrue(args) must have been returning something other than true and from that you know to start debugging its code to see why.
However, for this test to function, accessing the true property needs to make assertions and disrupt the code execution if the assertion fails (for example, by throwing an Exception).
Linters generally do not know how property access is done and would by default assume that the accessing of a property will not have any side-effects (i.e. alter the environment), especially not things like throwing exceptions (or cause other control-flow disrupting consequences).
Therefore, if you write something.true (without parentheses), the linter will assume that you simply want to access a property of something named true. If the expression goes nowhere (not used in an assignment, as function parameters, or in other evaluations), the linter would flag this a a potential typo or something else. Therefore, the previous valid case in chai will generate a linter error.
And these kind of linter errors can be useful in some cases because JavaScript does not complain if you try to access properties that do not exist. For example, what if you do have a typo in your test code?
chai.expect(someFunctionThatReturnsTrue(args)).to.be.ture
// ~~~~ not `true`, obviously a typo
This test would pass no matter what someFunctionThatReturnsTrue(args) returns. Because accessing .ture simply returns undefined and will not throw an error. Then you may miss some coding errors in someFunctionThatReturnsTrue() if you are forced to ignore these linter errors by chai.
Worse, if there is any error, you may assume that it's not in someFunctionThatReturnsTrue() because that test (wrongly) passes.
Thus the plugin, dirty-chai, by converting assertions by property access (like .true) into calling these properties as function (.true()) make sure linters will know that these statements can change the control flow:
chai.expect(someFunctionThatReturnsTrue(args)).to.be.true()
// Linter now knows you are expecting something to happen during `.true()` call
// and won't complain anymore
And now if you make a typo, the test will fail (by throwing an exception)
chai.expect(someFunctionThatReturnsTrue(args)).to.be.ture()
// ~~~~ Will throw something like
// `...to.be.ture is not a function`
Related
You can change the global variable of undefined to a value and you can read a property from a string, but you can't read the property of undefined.
The first two (the ones you can do) seem completely useless and the third is something I think most developers would love to work. If you make an API call and you're trying to handle complex data, why not just evaluate undefined.undefined.undefined.undefined = undefined instead of literally crashing the program? I know about optional chaining but I'm curious why is this even an issue. It seems like an easy fix but I'm guessing there is something idk about.
The point of errors in programming is that they inform you of a serious contract violation or illegal state. You expected something to be defined, but it wasn't. You expected something to be a certain type but it wasn't. Usually, the program shouldn't try to continue under a contract violation, and if it should, you'd want to make that explicit and at least log the violation rather than suppress it completely with head buried in the sand.
In many languages, these sort of invariant violations and illegal operations cause explicit errors, either at runtime or in compilation. The earlier you can catch the problem (thanks to a compiler error, warning or runtime crash/error during testing), the sooner and easier you can fix the bug.
JS has a reputation as stubborn to raise errors due to weak typing and other permissive design choices. You can concatenate and perform math on pretty much any types, or compare values with coercive equality operator == and wind up with anyone's-guess results. The reason is mostly historical: JS was designed for quick, simple scripts, not for massive single-page applications of today.
The reason technology like TypeScript exists is to essentially add errors to JavaScript to make programs safer and support the sort of large, industrial-grade applications that were never envisioned in 1995 when JS was designed. These errors are purely compile-time in the case of TS, but other languages like Python won't let you do many of the things JS does at runtime, like adding two different typed values "abc" + 42 which raises a TypeError (dictionary [a sort of Map] accesses also raise an error; you have to opt-in to the default value equivalent of undefined).
Simarly, JS' strict mode "eliminates some JavaScript silent errors by changing them to throw errors".
The contrary philosophy is to suppress errors, which is exactly what the optional chaining operator ?. offers, and it sounds like you're suggesting the default object property operator . should as well. This is syntactical sugar for code that returns undefined when an object doesn't exist, and it's nice to use occasionally to keep code terse and communicate intent, saying essentially "look, I realize this particular access might be on an undefined object, and my code is designed such that it expects that case and will continue on with the expression evaluating to undefined".
But it's a good thing this isn't the default behavior: it's essentially suppressing an error, pretty much the same rationale behind "abc" * 42 returning the default value NaN instead of throwing a type error in JS. Why would you ever want this? Probably never, hence JS's reputation as unsafe.
As I mentioned in the comments, React also made changes to ascribe to the "fail fast, fail loudly, fail hard" philosophy:
[...] in our experience it is worse to leave corrupted UI in place than to completely remove it. For example, in a product like Messenger leaving the broken UI visible could lead to somebody sending a message to the wrong person. Similarly, it is worse for a payments app to display a wrong amount than to render nothing.
[...] This change means that as you migrate to React 16, you will likely uncover existing crashes in your application that have been unnoticed before.
[...] We also encourage you to use JS error reporting services (or build your own) so that you can learn about unhandled exceptions as they happen in production, and fix them.
Here's advice from an article on javascript.info:
Don't overuse the optional chaining
We should use ?. only where it's ok that something doesn't exist.
For example, if according to our coding logic user object must exist, but address is optional, then we should write user.address?.street, but not user?.address?.street.
So, if user happens to be undefined due to a mistake, we’ll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
And here's a nice blog post on the matter
I'm loading a few environment variables into my Node & Express app using dotenv and a lot of these values are critical to the app's function so before doing anything I validate these variables to make sure they're included and that they have the correct type, otherwise I throw an error and stop the server from starting.
I now need to use these variables down the line, but TypeScript does not recognize that I have narrowed the types so it prevents me from doing things like process.env.test.includes (without optional chaining or nearby type assertion) saying Object is possibly null or undefined (I have strict mode enabled). I know in some cases it'll recognize that I have narrowed the type if I do type checks but I'm guessing this is not nearby enough to the code for it to pick up on this. Is there any way to signal that I have already narrowed the type from string | null | undefined to just string, for example, or do I have to continue to use the optional chaining operator?
You can use the non-null assertion operator ! to tell TypeScript that you know better, that it should never be null/undefined:
process!.env!.test!.includes
This is of course assuming that any of the keys in the chain could return null/undefined, but you probably don't need to use the operator on every single accessor.
I'm working on a JS library, as a part of it, I built a parser for small sort of a markup language to enable users to use more complicated expressions. I wanted to know if it is a good practice to throw an exception if there is an error in internal expression syntax and let users use the function within the try/catch block, or if it's better for example to return false and inform the user that there was a parsing error some other way (possibly console)? I can't really tell if throwing an exceptions is common practice when building a library, I know that jQuery for example throws some errors in its source, but jQuery is huge framework-like library so I would expect that, but is it common for small size libraries?
The problem with throwing exceptions on parse errors is that once you throw an exception, it is very difficult to resume the parse. That means that the parser intends to give up as soon as it hits the first syntax error; in other words, that it expects the user to fix one error at a time.
It's possible that you currently don't do any error recovery, so this might not bother you. But it should.
Are you certain that you're not going to want to try error recovery, particularly after listening to your users grumbling about how they have to try the parse dozens or hundreds of times to find all of their errors? And when you do eventually start to implement error recovery, will you be happy about having to find and change every place where the parser in order to change the way it is called?
const myError = new Error("bug");
myError.propertyIsEnumerable("message") //false
myError.propertyIsEnumerable("stack") //false
The implications of this are that when you for loop this kind of object, you won't fetch these fields, which I find strange.
What was the reasoning behind that?
The reason that I can think of is to maintain the behaviour of Object.keys. If you see the initial specs, the error part not very detailed. the error specs are
This specification specifies the last possible moment an error occurs. A given implementation may generate errors
sooner (e.g. at compile-time). Doing so may cause differences in behavior among implementations. Notably, if runtime
errors become catchable in future versions, a given error would not be catchable if an implementation generates the
error at compile-time rather than runtime.
An ECMAScript compiler should detect errors at compile time in all code presented to it, even code that detailed
analysis might prove to be “dead” (never executed). A programmer should not rely on the trick of placing code within
an if (false) statement, for example, to try to suppress compile-time error detection.
In general, if a compiler can prove that a construct cannot execute without error under any circumstances, then it may
issue a compile-time error even though the construct might neverbe executed at all.
You can see the first specs at https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%201st%20edition,%20June%201997.pdf
I am writing a Node.js application, and inside one of its code blocks various exceptions may be thrown (by 3rd party code I call). Now I want to react on these exceptions, hence I do:
try {
// Call 3rd party code
} catch (e) {
// Handle e
}
Basically, this works fine, but ... how do I differ between different exceptions?
They all unfortunately have Error as constructor, hence this is no viable way. I may use the message property, but of course this is not the nicest way (as I am dependent on the fact the the message will never change, which is - IMHO - more probable than that the constructor changes).
Any ideas?
PS: Concretely - I need to react on SSL error while trying to do a tls.connect. How do I detect that it's an SSL error?
Most errors that are system-level errors wrapped into javascript error objects will have a code property and errno you can compare against. The list is defined in uv.h in the node.js source code. That's probably your 2nd choice, with preference being
instanceof where possible
code or errno
message
But the fact is sometimes you just have to look at message. Given the dynamic and loose typing of javascript and the fact that exceptions in general don't play a big role in node.js, there will be cases where checking the message is the best you can do.
I would not recommend using a try/catch structure in Node, as I don't think it will work due to the asynchronous nature of Node (unless you're using basic, synchronous code).
Assuming you're utilizing asynchronous functions/packages, you'll probably have more luck checking the err status in a callback function.