Assertions in Java

Posted on September 30, 2018 by Rick Jelliffe

In Schematron, an assertion is a positive natural language statement about some aspect of a pattern that is expected to be found in an XML document.  It is implemented (to whatever extent possible) with the assert test.

However, in programming languages, assertions have a wider range of meaning. (To the extent that developers often just call them asserts to distinguish them.) Some are static, some are runtime, some for test-time, others disabled for production. Lets have a look at Java.

Lets distinguish five kinds of assertions in Java:

  1. Unit test assertions
  2. Java’s assert keyword
  3. Annotations
  4. Custom annotations
  5. Integrity methods

Unit Test Assertions

The closest thing to Schematron’s assertions is the kind of assertion found in a unit test. The most common unit test library for Java is JUnit. You define a series of tests and each one has one or more assertion.  A typical assertion might be

assertTrue("The value of i should be more than 2: found " + i, i>2);

If the assertion fails, the text string is computed and returned to the unit test Runner.  I have found that making the text string positive (like a schematron assertion) and putting in some diagnostic information (i.e. the actual found value of i) is the most effective use of the assertions.

Apart from assertTrue(), there is assertFalse() (upside-down logic like Schematron’s report) and fail().

Unit tests, like all testing, is determined by risk and effort.  Anything that is high potential risk (however that is defined for your project) or high actual risk (i.e. you had a specific bug, and you need to show it is fixed) really needs a unit test.  The less the risk, the more that the effort of a complete test will factor in: for something low risk,  a broad unit test (one which generates some false positives) that is simple to implement may be adequate.

Unit tests technology such as JUnit is not only useful for programmer unit testing. It can also be used for deployment and integration testing. A unit test can, for example, invoke a Schematron validation (for an example, see here.)

Java’s assert keyword

Java assert keyword can be used anywhere in a method. If it evaluates to false, it throws an AssertError runtime error (not a checked exception).  If you don’t supply -ea to the JVM, assertions are not enabled.

This kind of assertion:

  • lets the developer detect runtime errors before they cause trouble, to help debugging
  • documents the logic of the system
  • can be used by the language or IDE’s type checking system to enhance the normal declarations during static programming checking
  • can simplify the code, by establishing pre and post conditions of a method
  • should not be used for data validation, as the assertion will presumably be disabled in the production system

A Java assert can call a method that invokes a Schematron validation.

Canned Annotations

Annotations are now the preferred method in Java to provide assertion-like capability.  They can provide extra information for the compiler or static checking tools, or inject runtime checks of fields or methods.

Java comes with some standard annotation libraries that provide canned assertions.  These can be used particularly on the formal parameters of method declarations.  And each different framework provides various annotations. Annotations require the appropriate annotation processor to be active, otherwise they are just a kind of documentation.

The most common of these relate to whether some method parameter is allowed to be null or not. If you see @Nonnull or @NotNull in some Java code, that is what is going on.

However, you need to find out which class these belong to know the actual operation: is it a static constraint used for typechecking? Is it a runtime check that can be relied on for validation?  Is it a message to help the IDE. To see the details, check out Which Java @NotNull Annotation should I use?

For example, to allow javax.annotation.Nonnull you need to include a jsr305 JAR, while to use J2EE’s javax.validation.constraints.NotNull you need to include the javax.validation  validation-api  JAR file.

A highly regarded annotation library is the Checker Framework, which I haven’t used yet, but probably should. These allow a level of customizations, for example to check a string using a regular expression.

Custom Annotation

In Java, you can define your own annotations. By setting the Retention Policy, you can set whether they are for static information or runtime checks.  These are a way to make custom assertions.

You could, for example, make a custom annotation on a byte array that runs a Schematron validation. Or a file name, etc.

Integrity Methods

Apart from all these, sometimes it is useful just to make a method that performs a sanity check on the object, apart from general method calls. This can be called by any assertion method or test.  For example, we could define a boolean method isSane() and use it externally:

assert(x != null && x.isSane())