Well, I have mentioned a few times a little library I have been
hacking away at, and I thought I would give you guys an opportunity to
take a look. It's nothing special but I thought it might stimulate
discussion and has a few interesting parts to it. Most notably, fluent
API stuff and quite a lot of messing around with the new MethodHandle
stuff that Ben explained on the advanced Java course (and I say again,
well worth attending this if you have the chance). This does mean that
the library depends on Java 7.
I was stimulated into action on this based on a previous question I
asked when trying to find a library that could hammer Java methods to
test for correct parameter validation. It seemed nobody could name
one, so I set about putting something together myself. This was also
the reason for my question about random number generation that
generated so much pleasant discussion before.
I have dubbed it 'paragon' on the basis that, when using this library
all the usual PARAmeter tests are GONe... (See how clever I am?! ;)).
The general idea is that, you can write a test that looks like this...
(with appropriate static imports)
specification(receiver, "someMethodName", ReturnType.class,
Which will now call a method on the receiver instance with the signature:
ReturnType someMethodName(String, boolean)
...with a bunch of random values, some adhering to the restrictions
specified and some breaking them, and it will assert that
IllegalArgumentExceptions are thrown when the specification is broken
and are not thrown with the specification is followed.
Static methods are supported by passing a Class instance as the first
parameter, and constructors are supported by only passing a class
instance followed by the specs (i.e no name or return type).
There are specs for all primitive types and their corresponding
wrappers (e.g. integerSpec() for Integer and integerSpec().primitive()
for int), string, enum and email (as a string). Sensible fluent apis
are provided for each type. For example, the numeric types have
methods for setting min and max values, or alternatively there are
methods like positive() to allow only positive numbers (a max value
can also be provided).
I have also implemented a callback system that allows for receiving
callbacks as various parameter sets are tried. This is achieved by
passing an implementation of the 'SpecificationCallback' interface to
one of the specification methods, immediately before the specs. This
interface is a marker with no methods, but can contain methods
annotated with the following annotations:
@BeforeInvocation: A method with void return and no arguments, called
immediately prior to each invocation attempt.
@AfterInvocation: A method with a void return and no arguments, called
immediately after each invocation attempt.
@InvalidInvocation: A void method with arguments matching those of the
specification (method under test), called upon each invalid invocation
(parameters that are invalid according to the specification)
@ValidInvocation: A void method with arguments matching those of the
specification, and an additional argument of the return type (omitted
when testing void method).
I am not sure I am 100% happy with the callback system, but it is
useful for doing things like setting up mock stubbing and
expectations, so that it is possible to verify, using a mocking
library, that "When the method is called with valid parameters some
collaborator should be called like this, and when invalid parameters
are used, the IllegalArgumentException should be thrown without any
calls to a collaborator".
I have pasted up a simple example of the usage for a case where the
library is used to test a 'createAccount' service method that takes a
string email that must be non null, and turns this into an Account
object which is passed to a (mocked) repository, the repository
returns the saved Account which is then returned by the service
method. The example contains a callback for managing mocking....
Hopefully this shows that this is all not nearly as complex as I am
making it sound ;).
To be honest, it's mostly me just playing around with some of the new
features in Java 7, and I am confident I will will be the only actual
user of the library, but if anyone has any thoughts, wants to call me
crazy or tell me I have wasted my time, or even wants to suggest
things I could add or changes I could make (or, of course, spots any
bugs), I really am all ears... It's good to discuss this stuff.
So, finally, here it is.... git repository at:
You can also add
http://repository... to your maven
repositories and declare a dependency thusly:
Very interested to hear thoughts and opinions.