Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Programming with Assertions in Java Part 3

Sep 24, 2018 • 6 Minute Read

Introduction

In the previous guide, Programming with Assertions in Java Part 2, I discussed some best practices for using assertions and where not to use them.

In this guide, I'm going to talk about some misconceptions about assertions and wrap up the series.

Misconceptions About Assertions

Let's talk about some popular misconceptions about assertions.

Production Code Should Be Run with Assertions Turned On

Assertions are a development-time tool for testing purposes only. They are not meant for production environments. They validate your assumptions to help developers prevent errors down the line.

Why didn't the assumption hold true when testing this or performing that? Answer this question, find the root cause, and fix it. But do so in the development stages. The point of using assertions is to prevent those errors from happening by the time code becomes production-worthy.

Besides, remember that we are not supposed to use assertions to handle errors...which takes me to the next misconception.

Instead of Asserting a Condition, You Can Just Throw a RuntimeException

Sometimes yes, you can throw an exception instead of using an assertion. In fact, assertions are built on top of exceptions and an AssertionError can be caught like any other Java exception (not that we should do it).

But cases exist in which you cannot substitute an assertion with an exception.

Once again, assertions serve a distinct purpose from exceptions. They are intended to test assumptions and detect bugs. Meanwhile, exceptions indicate exceptional conditions - problems for which the developer may not have accounted.

You can use assertions for internal logic checks within your code, unchecked exceptions for error conditions outside your immediate code's control, and checked exceptions for business and recoverable errors.

Don't forget that assertions can be enabled or disabled (globally or for individual packages or classes).

If you really care if a particular process happens seamlessly, exceptions may be better.

For example, you can start by using an assumption to check that the execution flow doesn't reach a certain point of your program:

      switch(type) {
  case 1:
    //...
    break;
  case 2:
    //...
    break;
  case 3:
    //...
    break;
  default:
    assert false : "Default case reached";
}
    

Which is equivalent to:

      switch(type) {
  case 1:
    //...
    break;
  case 2:
    //...
    break;
  case 3:
    //...
    break;
  default:
    throw new AssertionError("Default case reached");
}
    

But with the advantage of having the option to disable the throwing of the exception.

And, as with customizable assert messages, it is better practice to use meaningful exceptions whenever possible:

      switch(type) {
  case 1:
    //...
    break;
  case 2:
    //...
    break;
  case 3:
    //...
    break;
  default:
    throw new IllegalStateException("Illegal type: " + type);
}
    

Unit Tests Are Substitutes for Assertions

In some ways, assert statements serve as tests of your code. However, unit tests should not substitute assertions and assertions should not substitute unit tests. Instead, assert statements can complement your unit tests.

Assertions validate assumptions about the internal state of your program - things like preconditions, post-conditions, and invariants.

In contrast, unit tests check the external behavior of your program. Generally, they follow the structure Arrange-Act-Assert. In other words, they set the preconditions (don't test them), perform the operation, and assert the result; all from an external perspective of the class or method being tested.

Yes, there can be some overlap between them. The assertions of the unit tests may be the same than the post-conditions assertions, but they have different purposes.

There's a limit of what you can do with assertion statements. Unit tests provide more flexibility and have a bigger scope than assertions, since they are specifically tailored to certain tasks. In fact, unit tests often drive the software development process, as seen by the test-driven development model.

Conclusion

The idea behind assertions is simple: a boolean condition is checked, it does nothing if it is true but throws an error if it is false.

Although it's a feature that is not used quite often (or is not very well-known, in some cases), it can turn out to be something helpful.

Assertions are more of a development-time tool, they can help you to discover an invalid condition that could be the cause of an error. In other words, they make sure that your application logic is correct.

How assertions are different from exceptions:

  • Exceptions can be caught, assertions are not meant to be caught.
  • Assertions can be disabled, exceptions cannot be disabled.

How assertions differ from unit tests:

  • Assertions check the internal state of your program, unit tests check the external behavior of your program.
  • Unit tests have a bigger scope than assertions. Unit tests can drive the software development process (Test-driven development).

Java assertions can be used to program using design-by-contract style. Eiffel is a programming language that tightly integrates design by contract in its construct. It doesn't use something like an assert keyword, but in Java, you can structure your logic, in the same way, using asserts.

In the JVM ecosystem, Groovy is an example of a language that has a more powerful support of assertions than Java. For example, they give a lot more information and they are enabled by default.

At the end of this Oracle's document about assertions, you can find a response in a FAQ section that states:

... Note also that assertions were contained in James Gosling's original specification for the Java programming language. Assertions were removed from the Oak specification because time constraints prevented a satisfactory design and implementation.

I wonder, would assertions had become more popular among Java programmers if they were more formal (like the Eiffel implementation) or with more features (like Groovy's)?

Give this guide a thumbs up if it was useful.