JUnit 5 was formally released in July 2016 and is quite a major evolution to JUnit 4 which has been the standard unit testing framework since Android first appeared on the scene. There are some quite significant changes and getting things set up, and then getting the best out of JUnit 5 can require a little effort, but the results are well worth it. In this loosely-coupled series we’ll explore a few distinct aspects of JUnit 5 and look at how we can get the best out of it. In this article we’ll look at some of the new features in JUnit 5 and see how they can simplify our tests.
At the end of the last article we’d got our tests much smaller and easier to read and mentioned that an obvious way that we could reduce the size further would be to utilise lambda expressions. JUnit 5 has been specifically designed to work with some of the new features in Java 8 and a lambda expressions are one of the key features that it can benefit from. However utilising these within Android projects is not straightforward, and in this article we’ll explore some strategies for using them.
The first and most obvious way of using lambda expressions in our test would be to switch to using Java 8 and use them natively, but this is not as easy as it sounds. Using the Android gradle plugin we are restricted to Java 7. Although some Java 8 features are supported in Nougat and lambda expressions are even available to earlier API versions, enabling these currently requires switching from the traditional ‘javac’ build tool chain to the ‘Jack’ toolchain. This is not a step to be taken lightly as Jack completely alters the entire build pipeline and although an APK gets produced at the end, may of the intermediate outputs are changed completely. This may not sound like a big deal until you consider that many static analysis tools will inspect the
.class files to generate their reports. No
.class files will be produced when using Jack so these tools will no longer run. There may also be issues with some annotation processors which may also have dependencies specific to the workings of the ‘javac’ toolchain. Moreover, Jack has actually been deprecated and Java 8 feature support is coming to the older ‘javac’ toolchain,
but unfortunately it is not here yet and is available in Android Studio 2.4 preview 4, but this is not a stable release so I would be reluctant to use it for production code just yet.
Another possibility is to use Retrolambda – which is a back-port of lambda expressions which works back to Java 5. Many projects which use RxJava often use Retrolambda because the two work nicely together and lambda expressions certainly help the fluency of implementing many RxJava patterns. If your project is already using Retrolambda then it is a no-brainer to go with that because you’ll get lambda expressions in your tests for free. However, if you’re not already using Retrolambda then it feels a little like using a sledgehammer to crack a nut.
Another possibility is to use Kotlin. Once again, this may be a little overkill if you are not already using it, but is another option for using lambdas.
Just in case there is any confusion it is probably worth pointing out that Retrolambda will effectively generate the necessary code which will get compiled by the Java 7 compiler in to Java 7 bytecode. Similarly, the Kotlin compiler will take
.kt source files and produce Java 7 bytecode. So, in both cases Java 7 bytecode is produced and this is what the ‘javac’ toolchain requires which is why both Retrolambda and Kotlin can be used with legacy projects.
For the project I’m working on we had some discussions about the correct route to go. We are using neither Retrolambda nor Kotlin. We felt that the Kotlin route offered us greater benefit. The developers on the project mainly have little or no experience of Kotlin yet there may be motive to start using it in the project in the medium to long term. By adopting it now for tests it provides the opportunity to learn Kotlin without risk to the main production codebase. This does not mean that we do not consider our test codebase as important as our production codebase – our tests code is very much a first class citizen, it just felt that there was less risk of introducing production bugs by limiting our use of Kotlin in this way.
In the next article we’ll look at how to enable Kotlin just for test code, and begin reaping the rewards of doing so!
There is no additional code for this article, but the previous code is available here.
© 2017, Mark Allison. All rights reserved.
JUnit 5: Lambdas by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.