An assertion is a simple pass/fail test of some condition, performed while your application is running. Assertions can be used to “sanity check” your code anywhere you believe certain conditions are guaranteed by correct program behavior. Assertions are distinct from other kinds of tests because they check conditions that should never be violated at a logical level: if the assertion fails, the application is to be considered broken and generally halts with an appropriate error message. Assertions are supported directly by the Java language and they can be turned on or off at runtime to remove any performance penalty of including them in your code.
Using assertions to test for the correct behavior of your application is a simple but powerful technique for ensuring software quality. It fills a gap between those aspects of software that can be checked automatically by the compiler and those more generally checked by “unit tests” and human testing. Assertions test assumptions about program behavior and make them guarantees (at least while they are activated).
If you have programmed before, you may have written something like the following:
if
(
!
condition
)
throw
new
AssertionError
(
"fatal error: 42"
);
An assertion in Java is equivalent to this example, but is performed
with the assert
language keyword.
It takes a Boolean condition and an optional expression value. If the
assertion fails, an AssertionError
is thrown,
which usually causes Java to bail out of the application.
The optional expression may evaluate to either a primitive or object type. Either way, its sole purpose is to be turned into a string and shown to the user if the assertion fails; most often you’ll use a string message explicitly. Here are some examples:
assert
false
;
assert
(
array
.
length
>
min
);
assert
a
>
0
:
a
// shows value of a to the user
assert
foo
!=
null
:
"foo is null!"
// shows message "foo is null!" to user
In the event of failure, the first two assertions print only a
generic message, whereas the third prints the value of a
and the last prints the foo is null!
message.
Again, the important thing about assertions is not just that they
are more terse than the equivalent if
condition, but that they can be enabled or disabled when you run the
application. Disabling assertions means that their test conditions are not
even evaluated, so there is no performance penalty for including them in
your code (other than, perhaps, space in the class files when they are
loaded).
Assertions are turned on or off at runtime. When disabled,
assertions still exist in the class files but are not executed and
consume no time. You can enable and disable assertions for an entire
application or on a package-by-package or even class-by-class basis. By
default, assertions are turned off in Java. To enable them for your
code, use the java
command flag
-ea
or -enableassertions
:
%
java
-
ea
MyApplication
To turn on assertions for a particular class, append the class name:
%
java
-
ea:
com
.
oreilly
.
examples
.
Myclass
MyApplication
To turn on assertions just for particular packages, append the package name with trailing ellipses (. . .):
%
java
-
ea:
com
.
oreilly
.
examples
...
MyApplication
When you enable assertions for a package, Java also enables all
subordinate package names (e.g., com.oreilly.examples.text
). However, you can
be more selective by using the corresponding -da
or -disableassertions
flag
to negate individual packages or classes. You can combine all this to
achieve arbitrary groupings like this:
%
java
-
ea:
com
.
oreilly
.
examples
...
-
da:
com
.
oreilly
.
examples
.
text
-
ea:
com
.
oreilly
.
examples
.
text
.
MonkeyTypewriters
MyApplication
This example enables assertions for the com.oreilly.examples
package as a whole,
excludes the package com.oreilly.examples.text
, and then turns
exceptions on for just one class, MonkeyTypewriters
, in that package.
An assertion enforces a rule about something that should be unchanging in your code and would otherwise go unchecked. You can use an assertion for added safety anywhere you want to verify your assumptions about program behavior that can’t be checked by the compiler.
A common situation that cries out for an assertion is testing for
multiple conditions or values where one should always be found. In this
case, a failing assertion as the default or “fall through” behavior
indicates the code is broken. For example, suppose we have a value
called direction
that should always
contain either the constant value LEFT
or RIGHT
:
if
(
direction
==
LEFT
)
doLeft
();
else
if
(
direction
==
RIGHT
)
doRight
()
else
assert
false
:
"bad direction"
;
The same applies to the default case of a switch:
switch
(
direction
)
{
case
LEFT:
doLeft
();
break
;
case
RIGHT:
doRight
();
break
;
default
:
assert
false
;
}
In general, you should not use assertions for checking the validity of arguments to methods because you want that behavior to be part of your application, not just a test for quality control that can be turned off. The validity of input to a method is called its preconditions, and you should usually throw an exception if they are not met; this elevates the preconditions to part of the method’s “contract” with the user. However, checking the correctness of results of your methods with assertions before returning them is a good idea; these are called post-conditions.
Sometimes determining what is or is not a precondition depends on your point of view. For example, when a method is used internally within a class, preconditions may already be guaranteed by the methods that call it. Public methods of the class should probably throw exceptions when their preconditions are violated, but a private method might use assertions because its callers are always closely related code that should obey the correct behavior.
Finally, note that assertions can not only
test simple expressions but perform complex validation as well. Remember
that anything you place in the condition expression of an assert
statement is not
evaluated when assertions are turned off. You can make helper methods
for your assertions that may contain arbitrary amounts of code. And,
although it suggests a dangerous programming style, you can even use
assertions that have side effects to capture values for use by later
assertions—all of which will be disabled when assertions are turned off.
For example:
int
savedValue
;
assert
(
savedValue
=
getValue
())
!=
-
1
;
// Do work...
assert
checkValue
(
savedValue
);
Here, in the first assert
, we
use the helper method getValue()
to
retrieve some information and save it for later. Then, after doing some
work, we check the saved value using another assertion, perhaps
comparing results. When assertions are disabled, we’ll no longer save or
check the data. Note that it’s necessary for us to be somewhat cute and
make our first assert
condition into
a Boolean by checking for a known value. Again, using assertions with
side effects is a bit dangerous because you have to be careful that
those side effects are seen only by other assertions. Otherwise, you’ll
be changing your application behavior when you turn them off.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.