Published on

Boost Your Java Unit Test Efficiency with LessIOSecurityManager: Control I/O for Faster and Safer Testing

Authors

Cross-posting from Wealthfront's Engineering blog.

Less IO for your Java Unit Tests with a SecurityManager

In this article, I will explain our goals and implementation of the open-sourced Apache-licensed Kawala LessIOSecurityManager, a Java SecurityManager implementation that spotlights I/O operations during your tests, while allowing fine-grained control of allowable operations via concise annotations.

Perils of hidden I/O operations

In our way of executing every possible execution path in our unit tests, we may inadvertently call methods that perform expensive I/O operations, either on the local disk, or across the network. A surprisingly expensive, yet omnipresent, example of a surprise-expensive operation that fails the obviousness test is the implementation of java.net.URL.equals(Object): comparing two instances of URL requires resolving any hostnames. Hostname resolution is a multi-millisecond operation that reaches across the network. Imagine performing equality-related operations to a large *List *with thousands of URL instances: each new hostname (thanks to Java's automatic DNS caching) would require a new domain name resolution requests. Many unit tests may trigger expensive and time-consuming operations to external resources with harmful side-effects: unit tests that slow down your iterations, or even unit tests that mutate their environment, infecting future tests with their toxic byproducts.

Design Goals of LessIOSecurityManager & Assurances Offered

Our goal with LessIOSecurityManager is to spotlight such I/O operations to the developer and provide assurances that a conscious decision will have to be made by the developer before any I/O takes place during unit tests. In the example of the URL class, a @AllowDNSResolution on the JUnit class containing the DNS-hungry unit test, would suffice to mend the CantDoItException that be thrown otherwise.

Fine-Grained Annotation-Based Configuration

Our fine-grained annotations, @AllowDNSResolution@AllowExternalProcess@AllowLocalFileAccess@AllowNetworkAccess, and @AllowNetworkMulticast, allow the developer to cast a perfectly-customizable I/O-awareness net with limited support for wild-cards and on-the-fly matching. The embedded JavaDocs accurately describe the possibilities for each parametrizable annotation, and our meta-tests, our tests for our testing infrastructure, serve as living documentation.

Implementation

Various methods in the core Java libraries that interact with the underlying system outside the JVM are hard-wired to check with the SecurityManager, if one is installed, before performing potentially hazardous operations. The SecurityManager contains a variety of check[...] methods that correspond to a variety of permission requests. The API is long and cumbersome, and theSecurityManager operates at such a low-level that erroneous states may be caused easily, rendering the JVM unable to load new classes. In our SecurityManager we use the following:

Installing the LessIOSecurityManager

Installing the LessIOSecurityManager is as easy as setting the java.security.manager system property. You can do that either by passing -Djava.security.manager=com.kaching.platform.testing.LessIOSecurityManager as a command-line argument to your java invocation. If you're using Ant, you may declare the java.security.manager system property in the element of your build.xml file. You must set the fork property to ensure a new JVM, with LessIOSecurityManager as the SecurityManager is utilized. (Take a look at the LessIOSecurityManager JavaDocs for an Ant instrumentation example.) Setting up the LessIOSecurityManager with IDEs or Maven is trivial.

Our Experience

During the instrumentation of our multi-thousand unit tests, we discovered a number of suspicious I/O operations and adjusted unit tests. The performance gains were insignificant compared to the extensive awareness around the side-effects of many commonly used operations that the LessIOSecurityManager brought to our team. We truly believe that the LessIOSecurityManager can help your engineering organization ensure that its tests perform no sneaky I/O operations and never mutate their environment. Feel free to leave comments here with questions, suggestions for improvements, or any bugs you may encounter.