Weapons for Boilerplate Destruction [Pt.3] — Testing The Annotation Processor

František Gažo
INLOOPX
Published in
4 min readJan 9, 2017

In case you missed it I recommend reading my previous post about building an annotation processor before you start indulging this entry.

In this post you’ll learn how to write tests for the annotation processor.

I’ll use the project from my previous post for this example.

Firstly, we’re going to add some dependencies to our build.gradle file.

  1. Guava is a set of core libraries.
  2. Truth is an assertion/proposition framework, that we’ll use instead of JUnit’s assertions.
  3. Compile-Testing is a library for testing javac compilation with (or without) annotation processors.
testCompile 'com.google.guava:guava:20.0'
testCompile 'com.google.truth:truth:0.30'
testCompile 'com.google.testing.compile:compile-testing:0.10'

We’ll also use an android stub library so that we don’t have to stub it ourselves.

testCompile 'com.google.android:android:4.0.1.2'

Now we can write some tests. These will be java unit tests, so we’ll put them inside the src/test/java/ folder.

We will either check generated java files

or error messages that should be thrown by the annotation processor.

INPUT_FILES and OUTPUT_FILES are lists of JavaFileObjects. ANNOTATION_PROCESSOR is an instance of the processor you are testing (you can optionally add more processors — e.g. I once needed to test if files generated by dagger2 are correctly processed by my processor). Lastly the ERROR_MESSAGE is a String that should be a part of the error message.

A simple test of valid @Logged usage could look like this:

LoggedTest.java

And a test of invalid @Logged usage could look something like this:

LoggedTest.java

As you can see, we created java files using JavaFileObjects.forSourceString(fullName, source) and then called the assertion. There’s not much to explain, but if you have any questions, please feel free to drop a comment.

When we run these tests we’ll get this output:

This was pretty simple, don’t you agree?

The only thing I found annoying is that we have to write a java file exactly how it will be generated. This doesn’t mean the spacing has to be the same — luckily empty lines or an additional white space is ignored when files are compared. However, this does mean that imports have to be in the correct order (and I noticed that sometimes a generated file contains e.g. import java.lang.String which shouldn’t be necessary). Also methods have to be in the correct order and you can’t just check if the generated class contains some specific code, you have to write the whole class.

I created some simple helper classes for managing the imports, so if you are interested in these, leave a comment. And of course if you know about a library that solves some of these issues please feel free to share your knowledge with me!

Now we’ll look at Spocka groovy testing framework — and how it will make our tests more readable to the human eye.

We’ll need to add 2 dependencies to our build.gradle file:

testCompile 'org.codehaus.groovy:groovy-all:2.4.7'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'

Spock has a really easy to use API and good docs so check it out. The main reason I like Spock is that it allows you to create data driven tests very easily (as you can see here). Also it makes your tests more readable thanks to the blocks it requires you to use (and also because you use groovy instead of java). It is not necessary to use assertion methods, you could just write expressions with a boolean return value. And when an error occurs in your tests, Spock presents them in an easy digestible form.

Spock test class has to extend spock.lang.Specification.

When we rewrite the first test method from before, we’ll get this:

LoggedSpecification.groovy

We got rid of Joiner and used a sentence as the method name. In addition to the test written in java we used Spock’s where block to create a data driven test with paramType. The paramType is also used in the test method name so we can distinguish between the tests.

The second test will look like this after a rewrite:

LoggedSpecification.groovy

When we run the tests now we’ll get this output:

I think it looks better than the previous one. What’s your opinion?

That’s all for this post. Even if you won’t create an annotation processor, I hope you’ll try Spock in some of your regular unit tests.

Spock has much more to offer than what I showed you here, so feel free to run wild through the docs ;)

If you have any questions, please leave a comment!

See you later.

--

--