How to assert for fun and profit

A hastily put together, hasty start guide to asserting
Published on Fri November 18, 2022 with tags: , , .
Updated on Fri November 18, 2022.

Recently, I had the (dis-)pleasure of using Java. In a function, as a responsible1 developer, I made sure to verify pre and post conditions via the assert keyword. Later, a caller violated one of the preconditions and, instead of raising a clear assertion failure, the program failed with a weird error further down the function.

This was puzzling to me. The bit of code that raised an error was the exact bit of code written with the assumption documented in the assertion I wrote, so something else must’ve been wrong, since the assertion passed.

Then it hit me, a realization from long ago: The entire Java ecosystem failed to understand assert and has been avoiding it using it, so much so, that neither IntelliJ IDEA, nor better tools, pass -ea to the JVM by default during development.

I wish to clear up some of the confusion surrounding assertions, and help more people use them effectively.

Note

This post won’t have code written in any particular language, more so pseudocode written in a syntax with nice highlighting support from Pygments. There is, however, some discussion of languages, since it inevitably plays a role in how you assert.

What is an assert?

So, if the Java folk managed to get it so wrong, what is the right approach? When do you use an assert and what meaning should it portray? What are the semantics of assert !this.getChildList().isEmpty();?

Fundamentally, to assert is to specify that, at the point in code where the assert is invoked, a condition MUST hold true, no matter what, and if it doesn’t, the code is broken in some way.

Each assertion, however, must satisfy a few properties, without which, it’s most likely assert abuse:

The pronunciation of assert

In math, it is common for an operation to not be pronounced the way it is spelled; for instance log2n is pronounced “base two logarithm of n”, or some variation of that, depending on who you ask. In computer science, a similar thing applies.

So, how is assert f(x); pronounced? Something along the lines of “The property f(x) must hold true, or else the program is malformed,” though, the pronunciation of “assert that f(x)” is more concise, if the person you’re talking to knows what it means to assert.

This pronunciation is how I want you, dear reader, to think about asserts, asserts are more effective comments, they are a checked and functional construct to state what your function is meant to do and what it expects to be able to do it, something much more powerful than a mere /* comment */.

Anticipated questions

What assertions should I write?

All the ones you feel necessary, really. Personally, I feel like it’s good practice to write down an assertion when I:

Picking when to assert is much like picking when to comment, except more explicit and more effective.

Why write a check that can be disabled?

Because your testsuite should be thorough enough to benefit from assertions being enabled, and end users not to (i.e, if you’re a library author, your assertions should still benefit the developers depending on your code, but not the end user that depends on their code).

Assertions can be expensive, non-asserting contract validation that is always on cannot: the former grants a very large freedom to do very rigorous verification.

I wouldn’t blame you for invoking Objects.requireNotNull on your arguments, but think about whether that’s better than an assertion for the end user. My position is that it’s not, since the user would be seeing precisely the same exception, manifesting in exactly the same way. Note that it’s not wrong to use both, as assertions can be far more powerful.

This kind of checking is, of course, more useful for languages which handle null more fatally, but I’d argue that most memory errors that are not result of all-out failure or corruption can be avoided through good use of C++, or other languages with highly expressive type systems2, in a much more effective manner than Java.

Are assertions not a tool for testing?

Yes and no, they are most useful during development and testing stages, but are not exclusive to testing, and especially not unit testing. A highly assert-dense codebase should grant you much greater confidence in your coverage, while making your code more expressive through the mere fact that you’re explicitly stating what must be the case at a given moment.

Want to discuss? Say hi by sending an email to ~arsen/public-inbox@lists.sr.ht [archives, etiquette].