20091013

Using source code generation to simplify test development with Quickcheck

With Quickcheck version 0.5 it is possible to use generated source code to remove repetitive test code.

The first generator tries to remove some redundancy from tests that use only a sample from a generator. For example a common source code pattern is:
import static net.java.quickcheck.generator.PrimitiveGenerators.strings;
String sample  = strings().next();
If you're only ever going to use this one string the next() call on the generator is annoying. You're doing it over and over again.

With the @Samples annotation on the PrimitiveGenerators class the PrimitiveGeneratorSamples class will be generated that contains exactly this code. So instead of the code above you can now write:

import static net.java.quickcheck.generator.PrimitiveGeneratorSamples.anyString;
String sample = anyString();

The same @Samples annotation can be used for your own generators. The setup is simple with javac. As long as you have the quickcheck-src-generator.jar in your classpath and did not disable annotation processing in your project source generation should work out of the box.

Only static methods returning a subtype of Generator<T> are supported. The generated class will be named <YourClass>Samples and the methods some<YourMethod in singular>. Your generator method names should be in plural as in the PrimitiveGenerators and CombinedGenerators classes. (The algorithm to create a singular is simple. It will remove the last character from the method name. So people() won't work. It will create somePeopl.)

A new way to create state characteristics is part of Quickcheck 0.5. The Iterables.toIterable factory method will create an adapter the Iterable interface. Iterable instances can then be used in for expressions.
for (Integer any : Iterables.toIterable(PrimitiveGenerators.integers())) {
  assertEquals(any * 2, any + any);
}
With the @Iterables annotation the source code to adapt to the Iterables interface gets generated. The generated adapted factory method can then be directly used in your tests:
import static net.java.quickcheck.generator.PrimitiveGeneratrsIterables.*;
for (Integer any : someIntegers()) assertEquals(any * 2, any + any);
This generator can be used with your generators, too. The same restrictions apply as for the @Samples annotation. The generated class is named <YourClass>Iterables and the methods some<YourMethodName>.

At the moment the two factory method classes CombinedGenerators and PrimitiveGenerators are annotated with @Samples and @Iterables, so all factory methods support now samples and for expressions.

The combination of source code generation and the Iterables adapter removes some pain with the missing closure support in Java. I've tried to circumvent this problem. The JUnit runner support is such a trial. But now after using it I do see that it does clutter the test code unnecessarily, it is harder to navigate and to refactor. So this feature will be probably removed from the Quickcheck release 0.6 as expressing the characteristic in an inner class and the new Iterable adapter are better alternatives.

No comments:

Post a Comment