When I started porting Quickcheck from Haskell I thought that there will be some closure-like facility in the Java language in 2 years. (After all more and more developers seemed to get exposed to the concept of functions and higher order functions. Most recent languages have some form of language construct to build new control structures in libraries.) So I did not bother myself too much with the usability problems that arise from the use of inner classes. In the meantime time it is obvious that Java will not have closures soon and maybe never will. So as long as we are using Java we have to get around this.
Quickcheck has now three different ways to express a characteristic: inner classes, annotations and for-loop expressions. Each of these solutions has its advantages and disadvantages. The example shows a characteristic defined for all three approaches.
@RunWith(QuickCheckRunner.class) public class IterableExample { @Test public void classic() { forAll(PrimitiveGenerators.integers(), new AbstractCharacteristic<Integer>() { @Override protected void doSpecify(Integer any) throws Throwable { assertEquals(any * 2, any + any); } }); } @ForAll(generatorMethod = "net.java.quickcheck.generator.PrimitiveGenerators.integers") public void annotation(Integer any) { assertEquals(any * 2, any + any); } @Test public void iterable() { for (Integer any : Iterables.toIterable(PrimitiveGenerators.integers())) { assertEquals(any * 2, any + any); } } }
Inner classes are the standard implementation and fine if one can ignore the boilerplate code that is used to express a characteristic as an inner class.
Annotations are not type-safe (and therefore refactoring safe), they cannot be navigated in IDEs and the generator definition and use is separated.
The Iterable adapter is type-safe and has concise definition of the characteristic but it lacks all features of the Quickcheck runner. So the Iterator<T> created by the Iterable<T> adapter will produce a fixed number of values. As the Java for loop construct is used the context information for the test run is missing. Setup and tear down have to be called explicitly.