20090625

Iterable adapter - Ways to define characteristics

The current development version of Quickcheck for Java contains new Iterable adapter. This adapter lets you convert a Generator implementation into an Iterable. This Iteraterable can then be used in Java for loops. This makes definitions of tests much shorter as no inner class has to be used to define a characteristic.

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.

No comments:

Post a Comment