JCheck Tutorial

Here is a short introduction on how to write specifications and test them with JCheck.

JUnit and JCheck

JCheck uses JUnit to function and any JUnit test case is essentially a JCheck test case and can be run with JCheck (and should work in the same way). You should be able to use JCheck in your favorite IDE or testing tool that supports JUnit 4 just as if it were JUnit. All one needs to do is to add the jcheck.jar to the classpath and let the fun begin!

A simple test

The JUnit Cookbook provides the following simple example of a JUnit test.


        class SimpleTest {
            @Test public void simpleAdd() {
                Money m12CHF= new Money(12, "CHF");
                Money m14CHF= new Money(14, "CHF");
                Money expected= new Money(26, "CHF");
                Money result= m12CHF.add(m14CHF);
                assertTrue(expected.equals(result));
            }
        }
        

Such a test is, of course, far from satisfactionary. What one probably wants to test is that Money.add(Money) works for all possible values. Such a test can of course be written, but would take quite some time to execute. A solution would be to test it with a (large) number of random values, if those tests are successful, then the code is probably sound (or at least it is more probable than with a single test case).

With JCheck this is very simple, the whole point beeing easy random testing. Simply add parameters to the method and a JUnit @RunWith annotation telling JUnit to use JCheck's runner instead of the standard one.


        @RunWith(org.jcheck.runners.JCheckRunner.class)
        class SimpleTest {
            @Test public void simpleAdd(int i, int j) {
                Money miCHF= new Money(i, "CHF");
                Money mjCHF= new Money(j, "CHF");
                Money expected= new Money(i+j, "CHF");
                Money result= miCHF.add(mjCHF);
                assertTrue(expected.equals(result));
            }
        }
        

Now the test method will be run a large number of times with random integers as parameters. As simple as that!

Controlling input - the easy way

Sometimes one needs to limit the possible input, for instance, if you are writing a sorting algorithm and wants to make sure it doesn't lose any elements you might want to test only arrays with sizes larger than one element.


        @RunWith(org.jcheck.runners.JCheckRunner.class)
        class SimpleTest {
            @Test public void simpleDivide(int[] somearray) {
                imply(somearray.length > 1);
                
                int expected= somearray.length;
                int result= MySort.sort(somearray).length;
                
                assertEquals(expected, result);
            }
        }
        

The call to imply will make sure that the test only is performed on input for which the implication holds. However some caution is needed - JCheck is random and will only try to generate new input parameters instead of failed ones a fixed number of times, you might get an "Arguments exhausted" exception if your selection is too narrow. In such a case it is best to write your own generator.

Controlling input - the slightly harder way

In some instances you need a very specific kind of input, or input of a type that JCheck doesn't support by default. In such a case you need to write your own generator.

For instance you might have an algorithm that only ever uses byte-arrays of size 2. In such a case using imply() would most certainly lead to an "Arguments exhausted", or at least take a very long time to execute. Better then to create a custom generator.


        class CustomByteGen implements Gen<byte[]> {
            public byte[] arbitrary(Random random, long size)
            {
                byte[] arr = new byte[2];
                random.nextBytes(arr);
                return arr;
            }
        }
        

        @RunWith(org.jcheck.runners.JCheckRunner.class)
        class SimpleTest {
            @Generator(klass=byte[].class,
                       generator=CustomByteGen.class)
            @Test public void byteArrTest(byte[] somearray) {
                // some kind of test
            }
        }
        

Using the @Generator annotation you can give a specific generator for a specific class, either on class level (used by all methods by default) or on method level (overrides any class level generator). For methods one can also specify a parameter position. If many different special generators has to be used one must use the @UseGenerators annotation with a list of generators.


        @RunWith(org.jcheck.runners.JCheckRunner.class)
        class SimpleTest {
            @UseGenerators({
                @Generator(position=0,
                           generator=CustomByteGen.class),
                @Generator(position=1,
                           generator=OtherCustomByteGen.class)})        
            @Test public void byteArrTest(byte[] somearray,
                                          byte[] someother) {
                // some kind of test
            }
        }
        

Controlling test

There exist a third way to control input - one that also allows to control how many tests are done, namely the @Configuration annotation. This annotation sets the maximum (and minimum) size of generated objects and how many tests are done (exluding those discarded by imply()).

The code above limits numbers to between -5 and 5 (and array sizes etc) and will run each test 10 times, except for method test2 that will be run a 100 times.


        @RunWith(org.jcheck.runners.JCheckRunner.class)
        @Configuration(tests=10, size=5)
        class SimpleTest {     
            @Test public void test1(int i) {
                // some kind of test
            }
            
            @Configuration(tests=100)
            @Test public void test2(int i) {
                // some kind of test
            }
        }