Pluralsight Logo
Author avatar

Esteban Herrera

Author badge Author

Programming with Assertions in Java Part 1

Esteban Herrera

Author BadgeAuthor
  • Sep 24, 2018
  • 9 Min read
  • 5,097 Views
  • Sep 24, 2018
  • 9 Min read
  • 5,097 Views
Java
J2EE

Introduction

You have probably used assertion methods when unit testing through frameworks like JUnit or third-party assertion libraries like AssertJ.

But did you know that Java has a built-in assert mechanism? If so, have you used it before?

Java's assertions were introduced way back in Java 1.4 and, like the assertion methods of unit testing frameworks, they use a boolean expression to throw an exception if it evaluates to false.

However, they can be used anywhere in the code (with a few restrictions due to some good practices) and with different semantics than the other assertion methods.

Assertions test things that you assume are true.

It may be things that you consider impossible to happen, things that must be present after executing a piece of code, or something that must be true for your code to work.

This may sound like a simple concept, but it's the key to understanding and using assertions properly.

In this series of guides, I'll show you how to use the Java assertion mechanism, the syntax and options to enable and disable assertions, good assertion practices, the Design by Contract style of programming using assertions, and how assertions differentiate to exceptions and unit tests.

Using Assertions

Let's start with the basics of how to use assertions.

Assertions use the reserved assert keyword. It has the following syntax:

1
assert booleanExpression;
java

Here's an example:

1
2
3
4
5
6
7
8
9
private double calculateInterest(double amount) {
  assert amount > 0 && amount <= 1_000;
  
  double interest = 0.0;
  
  // calculate interest
  
  return interest;
}
java

If the condition evaluates to false, an exception of type java.lang.AssertionError will be thrown.

This means that the above example is equivalent to the following:

1
2
3
4
5
6
7
8
9
10
11
private double calculateInterest(double amount) {
  if(amount > 0 && amount < 1_000) {
    throw new AssertionError();
  }
  
  double interest = 0.0;
  
  // calculate interest
  
  return interest;
}
java

As you might have guessed from its name, AssertionError is a subclass of Error: AssertionError class hierarchy

Why is AssertionError a subclass of Error rather than RuntimeException?

Because we are not supposed to catch Error and its subclasses.

It doesn't make sense to try to recover from assertion errors. Such an error would mean that the program is not operating under conditions assumed by the programmer to be true. This would make it very likely that continuing the execution will lead to more errors down the line.

So the error will cause the program to stop and a stack trace will be printed, for example:

1
2
3
Exception in thread "main" java.lang.AssertionError
	at com.example.AssertTest.calculateInterest(AssertTest.java:6)
	at com.example.AssertTest.main(AssertTest.java:16)

That may not be very useful.

However, an assert statement can also take a message:

1
assert booleanExpression : "Message about the error";
java

For example, if the following assertion fails:

1
2
3
4
5
6
7
8
9
private double calculateInterest(double amount) {
  assert 
    amount > 0 && amount < 1_000 
    : 
    "Amount should be between 0 and 1,000: " 
        + amount;
  
  // ...
}
java

Something like the following will be printed to the standard output:

1
2
3
Exception in thread "main" java.lang.AssertionError: Amount should be between 0 and 1,000: -11.0
	at com.example.AssertTest.calculateInterest(AssertTest.java:6)
	at com.example.AssertTest.main(AssertTest.java:16)

Once again, the above example is equivalent to:

1
2
3
4
5
6
7
8
9
  if(amount > 0 && amount < 1_000) {
    throw new AssertionError(
      "Amount should be between 0 and 1,000: " 
        + amount
    );
  }
  
  // ...
}
java

Most of the time, you'll use a String but, if you look at the constructors of AssertionError, you'll see that it can also take other types that will be converted to a string:

1
2
3
4
5
6
7
8
AssertionError(boolean detailMessage)
AssertionError(char detailMessage)
AssertionError(double detailMessage)
AssertionError(float detailMessage)
AssertionError(int detailMessage)
AssertionError(long detailMessage)
AssertionError(Object detailMessage)
AssertionError(String message, Throwable cause)
java

Which means that if that is enough for you, you can use something like:

1
2
3
4
5
private double calculateInterest(double amount) {
  assert amount > 0 && amount < 1_000 : amount;
  
  // ...
}
java

If the boolean expression evaluates to true, nothing will happen.

However, assertions are not enabled by default. So, even if the assertion fails, if you don't run your program with a special flag, nothing will happen.

Assertions are enabled with either the -ea or enableassertions flags:

1
2
3
java –ea AssertTest
// Or
java –enableassertions AssertTest

This would enable assertions in all the classes of our program except for Java classes (system classes).

If you want to enable assertions in Java classes, you can use the -esa or enablesystemassertions flags:

1
2
3
java –esa AssertTest
// Or
java –enablesystemassertions AssertTest

You can also enable assertions for just one class:

1
java –ea:com.example.MyOtherClass AssertTest

Or in a specific named package and any of its subpackages:

1
java –ea:com.example... AssertTest

Or in the default package in the current working directory:

1
java –ea:... AssertTest

There's also an option to disable assertions:

1
2
3
java –da AssertTest
// Or
java –disableassertions AssertTest

And the corresponding option to disable assertions in Java classes:

1
2
3
java –dsa AssertTest
// Or
java –disablesystemassertions AssertTest

You might be wondering, why do we have this option if assertions are disabled by default?

Well, you can use the same options of the ea and esa flags to disable just one class or entire packages.

And you can also combine them. For example, you can enable assertions in all the classes of the package com.example except for the class com.example.Utils:

1
java –ea:com.example... -da:com.example.Utils AssertTest

Design-by-Contract Style and Assertions

Java's assert mechanism can be used for an informal design-by-contract style of programming.

Design-by-Contract is an approach to designing software that views software as a set of components whose interactions are based on mutual defined obligations or contracts in the form of:

  • Acceptable and unacceptable input and return values or types
  • Error and exception condition that can occur
  • Side effects
  • Preconditions
  • Postconditions
  • Invariants

This concept was conceived by Bertrand Meyer, who designed the Eiffel programming language based on this and other concepts of object-oriented programming.

For example, in Eiffel, you can have a routine with the following syntax:

1
2
3
4
5
6
7
8
processElement (e : ELEMENT) is
  require
    not e.empty
  do
    -- Perform the operation
  ensure
    e.is_processed
  end
eiffel

Similar to assert, the require clause checks the input, or preconditions, while the ensure clause checks the output or post-conditions. Both of these conditions are part of the contract associated with this routine.

But we can also have conditions that must apply to the whole class all the time (like before and after calling one of its methods), not just at a certain moment. These are known as class invariants:

1
2
invariant
  element_count > 0
eiffel

You can think of invariants as a condition that is a precondition and a post-condition at the same time.

This way, Eiffel's IDE can provide a short form of the class in text format, without all the implementation of the class and just retaining the contract information:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass
feature
  processElement (e : ELEMENT) is
    require
      not e.empty
    ensure
      e.is_processed
    end
    
  -- Other features or routines
    
invariant
  element_count > 0
end
eiffel

We don't have clauses or documentation mechanism like these in Java, but the preconditions, post-conditions, and invariants are really some forms of assertions that can be implemented with the assert keyword.

You have seen examples of preconditions and post-conditions (just remember that it's not recommended to use assertions to check the input of public methods), but in the case of invariants, Java assertions don't particularly enforce class or any kind of invariants.

If you want to do something equivalent that Eiffel class invariants, the recommended approach is to create a private method that checks for the invariant conditions:

1
2
3
private  boolean checkClassInvariants() {
  // ...
}
java

And use this method in assert statements placed at the beginning or/and at end of public methods, constructors, or any other places you think it will make sense:

1
assert checkClassInvariants();
java

Next Steps

In the next guide, Programming with Assertions in Java Part 2, I'll discuss some best practices when using assertions.

5