Create Instances of Annotations (for UnitTests)

Usually you don’t have to test Annotations. But you might want to test the effects of your Annotations and (given an Annotation with parameters) this can get quite awkward if you try to write classes or methods for every variant to annotate and test.

Given an Annotation like this one:

1
2
3
4
5
6
7
8
9
10
11
12
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Lister {
    int position() default -1;
    String headerKey() default "";
    String displayKey() default "";
    boolean editInPlace() default false;
    boolean escapeModelString() default true;
    @SuppressWarnings("rawtypes")
    Class<? extends ILabelProvider> customLabelProvider() 
            default ILabelProvider.class;
}

There are countless combinations to test. It would be easy if new Lister() would work, but it doesn’t (otherwise this article wouldn’t make any sense at all). There are several solutions for this… What I’d call a plain core Java solution, an easy workaround, a little hack and my favourite. I’ll describe them all so you can take your pick.

The plain core Java solution:

You can create a class implementing java.lang.Annotation and your Annotation-interface. Using eclipse, you’ll have to provide the fully qualified classname of your Annotation in the implements-clause. This will reduce the compile error to a warning stating The annotation type Lister should not be used as a superinterface for [yourclass] but that shouldn’t bother you since you know, what you’re doing or at least that’s what you tell your code reviewing peer or boss.

You’ll have to implement all the methods defined by your Annotation and the Annotation interface, so that can be a little bit of work but eclipse will do most of the job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class TestLister implements net.unbewaff.wicketcrudr.annotations.Lister, Annotation {
    private int position = -1;
    private String headerKey = "";
    private String displayKey = "";
    private boolean editInPlace = false;
    private boolean escapeModelString = false;
    @SuppressWarnings("rawtypes")
    private Class&lt;? extends ILabelProvider&gt; customLabelProvider = ILabelProvider.class;

    /**
        * @param position
        * @param headerKey
        * @param displayKey
        * @param editInPlace
        * @param escapeModelString
        * @param customLabelProvider
        */
    public TestLister(Integer position, String headerKey, String displayKey, 
            Boolean editInPlace, Boolean escapeModelString, 
            @SuppressWarnings("rawtypes") Class<? extends ILabelProvider> customLabelProvider) {

        if (position != null) {
            this.position = position;
        }
        if (headerKey != null) {
            this.headerKey = headerKey;
        }
        if (displayKey != null) {
            this.displayKey = displayKey;
        }
        if (editInPlace != null) {
            this.editInPlace = editInPlace;
        }
        if (escapeModelString != null) {
            this.escapeModelString = escapeModelString;
        }
        if (customLabelProvider != null) {
            this.customLabelProvider = customLabelProvider;
        }
    }


    @Override
    public int position() {
        return position;
    }

I’ll leave most of the methods to your imagination since they’re just getters with a wrong name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    @Override
    public int hashCode() {
        final int prime = 127;
        int result = 0;
        result += prime * customLabelProvider.hashCode() ^ ((customLabelProvider == null) ? 0 : customLabelProvider.hashCode());
        result += prime * displayKey.hashCode() ^ ((displayKey == null) ? 0 : displayKey.hashCode());
        result += prime * editInPlace.hashCode() ^ (Boolean.valueOf(editInPlace).hashCode());
        result += prime * escapeModelString.hashCode() ^ (Boolean.valueOf(escapeModelString).hashCode());
        result += prime * headerKey.hashCode() ^ ((headerKey == null) ? 0 : headerKey.hashCode());
        result += prime * position.hashCode() ^ (Integer.valueOf(position).hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) 
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TestLister other = (TestLister) obj;
        if (customLabelProvider == null) {
            if (other.customLabelProvider != null)
                return false;
        } else if (!customLabelProvider.equals(other.customLabelProvider))
            return false;
        if (displayKey == null) {
            if (other.displayKey != null)
                return false;
        } else if (!displayKey.equals(other.displayKey))
            return false;
        if (editInPlace != other.editInPlace)
            return false;
        if (escapeModelString != other.escapeModelString)
            return false;
        if (headerKey == null) {
            if (other.headerKey != null)
                return false;
        } else if (!headerKey.equals(other.headerKey))
            return false;
        if (!Integer.valueOf(position).equals(Integer.valueOf(other.position)))
            return false;
        return true;
    }
}

You'll need to pay attention to the implementation of equals and hashCode since the implementation differs from what eclipse generates by default. The Javadoc for this explains the procedure.

"The hash code of an annotation member is (127 times the hash code of the member-name as computed by String.hashCode()) XOR the hash code of the member-value, as defined below:"


Pro: Plain Old Java, easy to write and easy to understand

Con: Much code to write, needs to be updated whenever your Annotation changes, need to follow unusual implementations of equals and hashCode, warnings


The easy workaround

Just wrap it up. Create a class, add a member of your Annotation-Type, (in eclipse) right-click->Source->Generate Delegate methods... and implement them. Then continue to extract an Interface. That will look pretty similar to the definition of your Annotation but that's the point. Now you can change your consumers to accept the new interface instead of your Annotation and run your tests with mock implementations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ListerWrapper implements IListerWrapper {

    private Lister internal;

    /**
     * @param internal
     */
    public ListerWrapper(Lister internal) {
        this.internal = internal;
    }

    /* (non-Javadoc)
     * @see net.unbewaff.wicketcrudr.annotations.IListerWrapper#position()
     */
    @Override
    public int position() {
        return internal.position();
    }

Again, most of the implementation is left to your imagination. For testing you can use use any other implementation of your interface.

Pro: Plain Old Java, easy to write and easy to understand

Con: Much code to write, many quite useless classes and interfaces, needs to reflect changes to your Annotation


The little hack

An Annotation is in fact (as can be seen in the first solution) an Interface that can be implemented. But it can be proxied as well...

1
2
3
4
5
6
7
8
{Lister.class, Annotation.class}, new InvocationHandler() {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // <TODO do your stuff here
            return null;
        }
    });

This Proxy can be used just like the Annotation. All the magic (like your mock-code) goes into the invoke method. Just make sure, your proxy implements the Annotation-interface as well as your custom Annotation.

Pro: Flexible, short, no additional classes, just has to reflect changes it it matters to the consumer in test.

Con: Reflection


My favourite

The description of my favourite solution is quite short and in reality it's just a variant of the solution above. If it's an Interface, it can be proxied. It it can be proxied, it can be mocked.

1
Lister l1 = mockery.mock(Lister.class);

Creates a perfectly usable Mockobject to your Annotation, just waiting for the Expectations to be added

Pro: Flexible, short, no additional classes, additional testing possibilities with Expectations

Con: needs jMock