Assertions
It is not uncommon that many sections of code will only work if certain conditions are met. Sometimes, these preconditions are not immediately obvious until you actually unpick the code to work out what’s going on.
Please welcome to the stage “assertions” - statements which allow you to test assumptions inside your code. They are available in both Typescript (JavaScript) and Java and they are a clean and clear way of helping to self document the inner workings (outputs) of code.
An assertion simply tests a boolean condition to be true. That’s it. It makes the assumptions contained within your code explicit and clear. Whereas unit tests document the external state of a method, asserts document and test the internal state of a method to ensure that the data structures are correct.
Using assertions
If you expect something to be true, document it with an assertion. As a diagnostic tool they are simple and straight forward checks of program execution and logically impossible situations. Whereas an handled exception may be possible, assertions should be testing the impossible. If an assertion error is thrown, then there is some kind of error in the program.
This is a basic example of a switch statement with a logically impossible default case and which serves as a perfect example of how an assert can be used:
switch(suit) {
case Suit.CLUBS:
...
break;
case Suit.DIAMONDS:
...
break;
case Suit.HEARTS:
...
break;
case Suit.SPADES:
...
break;
case default:
assert false: suit;
}
In this case, it tells us thatsuit
should only equal one of the the four values (CLUBS
, DIAMONDS
, HEARTS
, SPADES
). Although simplistic, this demonstrates the simplicity and effectiveness of assertions.
What not to do
DO NOT use assertions to control code or change the behaviour of code. That is, do not use assertions to do any of the work that your application requires for correct operation. Instead, use assertions to test that your code is correctly operating as intended. This is known as testing for invariants. For example, if something should never be returned as null (or undefined) then test it with an assertion. However do use it to test some logical condition. For example do no do this:
// uurgh...bad
assert(age >18 && age < 100)
// better
if (age <= 18 && age >= 100)
//handle this condition
Assertions are not a replacement for error handling. Given that they always required to be true, they should be written as unchecked errors indicating a programmer error.
In Java (for example), assertions could be turned off at compile time and correct program execution should not be impacted in any way. This could make sense because there is some overhead to running assertions. On the other hand, it may make sense to leave assertions in. Why? In testing there is no real way to know exactly how a piece of code will be used. Assertions can be used to try and detect where programmer errors have been missed. Leave optimisation until you need it. In the meantime, leave assertions in place.
Asserting public method input parameters is also not the right place for assertions. Instead, the method contract should be documented inside the documentation of the method and any incorrect inputs should be handled by the appropriate runtime exception handling code. Using an assertion in this situation simply would not give the appropriate information to the calling program in the case where assertions were turned off.
JavaScript and Java
In Java, an assertion failure gives rise to an AssertionError. That’s simply. For JavaScript console.assert running inside the browser, the assertion error is printed to the console. Inside the NodeJS environment an AssertionError is thrown.
More reading
« Promisifying your XMLHttpRequest
I like Typescript »