OSGi testing with Gradle and Pax Exam
I've recently been working on a Spring issue which involved writing a Gradle-built test to ensure that the Spring framework 3.1.x bundles successfully resolve in an OSGi container.
The Gradle side of this was a very simple and pleasant experience, as the following extract of the file build.gradle shows:
apply plugin: 'java' apply plugin: 'eclipse' repositories { mavenCentral() } dependencies { ... }
There were minor usability issues1 with the Gradle Eclipse plugin, but I gather these will soon be resolved.
I decided to use Pax Exam to launch the testcase inside Equinox, and this require specifying some dependencies in build.gradle:
dependencies { paxExamVersion = '2.3.0.M1' equinoxVersion = '3.6.0.v20100517' testCompile "junit:junit:4.+", "org.ops4j.pax.exam:pax-exam-junit4:$paxExamVersion", "org.eclipse.osgi:org.eclipse.osgi:$equinoxVersion", "javax.inject:javax.inject:1" testRuntime "org.ops4j.pax.exam:pax-exam-container-native:$paxExamVersion", "org.ops4j.pax.exam:pax-exam-link-mvn:$paxExamVersion", "org.ops4j.pax.url:pax-url-aether:1.3.5" }
Let's go through the dependencies in turn:
- JUnit 4 is the test framework.
- @RunWith(JUnit4TestRunner.class) marks the test for launching under Pax Exam.
- Equinox provides OSGi types at compile time and an OSGi container at runtime.
- A bundle context is injected into the test using @javax.inject.Inject.
- The test uses the native Pax Exam container - all the tests run in a single JVM.
- The test uses Pax Exam to install Spring bundles directly from Maven repositories.2
- The test caches bundles from the SpringSource Enterprise Bundle Repository using the Pax URL Mvn protocol.
The Pax Exam developers kindly helped me sort a basic setup issue which wasn't too obvious from the documentation. Thanks Harald and Toni! I needed to explicitly install a JUnit bundle, by calling junitBundles from the configuration method:
@Configuration public static Option[] configuration() throws Exception { return options(// provisionSpringBundle("spring-core"), // provisionSpringBundle("spring-beans"), // // etc. junitBundles()); }
The full source of the project is available on github (under the Apache 2.0 license) for anyone who wants to give it a spin or use it for their own purposes. If you'd prefer to write your tests in Groovy, take a look at Hamlet D'Arcy's blog.
Footnote:
1: The generated .classpath contains absolute paths rather than paths relative to the project and so is not suitable for committing and sharing with others. Also re-running gradle eclipse without first deleting .classpath can result in it containing duplicate paths. Luke Daley tells me that both these problems will soon be fixed, the latter fix appearing in gradle 1.0 milestone 7.
2: If the testcase was intended a a stand-alone test of published versions of Spring, it would cache Spring bundles in the local Maven repository for efficiency. But we plan to integrate the testcase into the Spring build in due course and so we'll want to grab the latest built version each time to cope with re-spins, so cacheing isn't helpful.