Popper extends JUnit to allow you to specify theories. Theories are assertions about your code's behavior that may be true over infinite sets of input values. You might find it useful to pose theories about your Groovy code too.
Let's consider how we might test the following class (example taken from the Popper web site):
With traditional JUnit code, we might test it as follows:
This tests the method for one
amount value and one
m value. Next steps might be to triangulate so that additional values are also tested. In general though, it might be difficult to know when you have done enough values (when to stop) and also what invariants of your class may hold if you simply keep adding more tests without sufficient refactoring. With these factors in mind, Popper provides facilities to make invariants and preconditions of your classes obvious as well as providing an extensible framework for adding new test values.
Here is how you might use Popper to test the above class. First, we have avoided using Hamcrest style assertions in our Groovy code. Groovy's built-in
assert method usually allows such assertions to be expressed very elegantly without any additional framework. We'll create a small helper class to allow Groovy-style assertions to be used for method pre-conditions:
Now, our test becomes:
We have added an additional log variable to this example to explain how Popper works. By default, Popper will use any public fields in our test as test data values
VAL4 in our example. It will determine all combinations of the available variables and call the
multiplyIsInverseOfDivide() for each combination. This is a very crude way to select test instance values but works for simple tests like this one.
You should also note the
assume statement. In our example, we haven't catered for
m being 0 which would result in a divide by zero error. The assume statement allows this method precondition to be made explicit. When Popper calls the test method, it will silently ignore any test data combinations which fail the method preconditions. This keeps the preconditions obvious and simplifies creating test data sets.
Here is the output from running this test:
We wouldn't normally recommend sending this kind of information to standard out when running your test, but here it is very illustrative. Note that all four test values have been used for the
amount variable but only three values have been used for
m. This is exactly what we want here.
Popper supports an extensible framework for specifying more elaborate algorithms for selecting test data but they require annotations on method parameters. Support for annotations on Groovy method parameters is still in the works but is expected to be completed shortly. Once that is in place, this example will be extended to show these other Popper features in action.