Is the assert (a language feature) worth using?
A useful tool for setters and getters which are very rarely unit tested. Â Many language docs don't recommend their use for public methods as these methods should be testing the parameters to these methods as a matter of course, but setters and getters, well!!!
Standards bodies like C++11 and the JCP recommend certain patterns of use for the assert keyword in their languages. Â The use of assert should not negate the use of unit testing tools like JUnit and NUnit. Â In actual fact I strongly recommend these tools as a better choice than assert. Â Several recommend uses are
- when you have written a comment that asserts an invariantÂ
- to indicate the flow control through software
- to indicate preconditions to private methods
- to indicate postconditions to methods (private and public)
- to establish class invariants, what must be true about each new instance of a class
- to indicate preconditions concerning whether or not a given lock is held
Let's examine each one
When you have written a comment that asserts an invariant
In the examples below, the % operator returns the remainder of the division calculation
if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else { // We know (i % 3 == 2) ... }
Would be better written as
if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else { assert i % 3 == 2 : i; ... }
When you look at the first eample, we know that the else statement is actually an assumption and could lead to a major bug at a later date.
To indicate the flow control through software
Use the assert to indicate that your code has reached a point where it should not have reached
void foo() { for (...) { if (...) return; } // Execution should never reach this point!!! }
Would be better written asÂ
void foo() { for (...) { if (...) return; } assert false; // Execution should never reach this point! }
Preconditions on private methods (never use asserts on public methods) - a type internal invariant
By convention, preconditions on public methods are enforced by explicit checks that throw particular, specified exceptions. For example:
/** * Sets the refresh rate. * * @param rate refresh rate, in frames per second. * @throws IllegalArgumentException if rate <= 0 or * rate > MAX_REFRESH_RATE. */ public void setRefreshRate(int rate) { // Enforce specified precondition in public method if (rate <= 0 || rate > MAX_REFRESH_RATE) throw new IllegalArgumentException("Illegal rate: " + rate); setRefreshInterval(1000/rate); }
In the above code fragment, the "setRefreshRate" has code to ensure that the rate passed in is within a certain set of boundaries. Â With public methods you have no control over what is passed into the method so it is incumbent on the developer to verify the input values.
/** * Sets the refresh interval (which must correspond to a legal frame rate). * * @param interval refresh interval in milliseconds. */ private void setRefreshInterval(int interval) { // Confirm adherence to precondition in nonpublic method assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval; ... // Set the refresh interval }
The private method which is not part of the classes public contract has an assert to ensure that the value passed in is correct. Â Why here and not on the public method. Â Using an assert on the public method would be erroneous because in release mode asserts are disabled therefore the test would not be executed and the release version would continue to run as if everything is ok.
Private methods are called from public methods. Â They are used to either set values on objects or perform some kind of functionality that shouldn't used publicly (usually derived through functional decomposition or refactoring). Â More often than not they are written in a way where they expect correct value types and data ranges to be passed to them (usually because an extensive level of verification has taken place in the calling method so why repeat it). Â But there is no guarantee that the correct data will be passed, so an assert is a good way of indicating that a wrong value has been passed in from a public method.
To indicate postconditions on methods (private and public)
/** * Returns a BigInteger whose value is (this-1 mod m). * * @param m the modulus. * @return this-1 mod m. * @throws ArithmeticException m <= 0, or this BigInteger *has no multiplicative inverse mod m (that is, this BigInteger *is not relatively prime to m). */ public BigInteger modInverse(BigInteger m) { if (m.signum <= 0) throw new ArithmeticException("Modulus not positive: " + m); ... // Do the computation assert this.multiply(result).mod(m).equals(ONE) : this; return result; }
Notice that the assert is used on a public method and it is the last statement bar the return statement. Â We don't want the error being created in release mode so using in development mode we should hopefully trap anything not meeting the postcondition. Â Again, there is no guarantee of this occurring if the test is never executed that would cause 1) this method to be called and 2) for the correct test values to be passed in. Â Some would argue that if you have written good unit tests then there would be no need for asserts at this level.
Establish class invariants - a type of internal invariant
An internal invariant that applies to every instance of a class at all times except when an instance is transiting between states. Â It is essentially a check on the combined value of a set of attributes within the object, this is more conveniently described as the state of the object. Â State is defined as a measure of what of what an object can be at specific points in time. Â So really when you use an assert against an object what you are attempting to do is assert against the state of the object. Â
In a development environment trapping such an error is crucial but in a release environment you would possibly want the code to operate in a degraded mode. Â An example of this would be a framework that implements a communications connection using a number of different transport mechanisms. Â This comms framework can switch between available connections at any time during program execution. Â The expected transport mechanism will be a web service but if it cannot be established ftp is available with some caveats such as speed and security etc. Â You have created a wrapper around the connection framework that allows you to force the switch between different transport layers. Â Ftp should only be used during development. Â So in actual fact the only status transport layer that you should be working with at all times is "web service". Â
The connections framework has two methods available, getConnectionType():String returning the type of connection as a string, "webservice" or "ftp" and isConnected(): boolean, indicating whether a connection is even available.
class MyWrapper { private CommsFramework cf = new CommsFramework(); private boolean isWebServiceAvailable() { return cf.getStatus().equals("webservice") ? true : false; } // all operations that will read and write from the CommsFramework will do the following { try { assert isWebServiceAvailable(); } catch( AssertionError ae ) { // operate in degraded mode and shutdown gracefully ... } } ... }
The assert is used to ensure that we only run the code if the there is a webservice connection.
Yes this is somewhat a contrived example but it aims to show a possible use of assert. Â The problem is if we disable the assert feature this could would not compile. Â With all of the examples that I have shown and with the use of tools like JUnit and NUnit I would argue that there is no real reason to use assert, pre and post conditions can be tested through with well thought out tests and object state can definitely be tested through unit tests. Control flow might be a possible use as this is difficult to test with unit tests.
To indicate preconditions concerning whether or not a given lock is held
There maybe one situation where the assert may be a useful tool.
Classes designed for multithreaded use often have non-public methods with preconditions relating to whether or not some lock is held. For example, it is not uncommon to see something like this:
private Object[] a; public synchronized int find(Object key) { return find(key, a, 0, a.length); } // Recursive helper method - always called with a lock on this object private int find(Object key, Object[] arr, int start, int len) { ... }
A static method called holdsLock
 has been added to the Thread
 class to test whether the current thread holds the lock on a specified object. This method can be used in combination with an assert
 statement to supplement a comment describing a lock-status precondition, as shown in the following example:
// Recursive helper method - always called with a lock on this. private int find(Object key, Object[] arr, int start, int len) { assert Thread.holdsLock(this); // lock-status assertion ... }
Conclusion
Al in all, I really do feel that you would be better of writing unit tests using something like JUnit and NUnit, and writing Acceptance level tests using something like fitnesse.