A simple Pax Exam JUnit test

While JUnit is not the only way of using Pax Exam, it is probably the one that most users prefer, so let us look at a simple piece of source code.

If you are using Maven, the easiest way to write pax-exam tests is to make them integration tests; put them it in a separate integration-test Maven module that runs in your build after your OSGi bundle(s) are built. There's more explanation of this below.

After explaining the meaning of the Pax Exam annotations and APIs you see in this snippet, we'll discuss how to set up your environment for creating and running your own Pax Exam tests.

package com.example.myproject.test;

import static org.junit.Assert.*;
import static org.ops4j.pax.exam.CoreOptions.*;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.regression.pde.HelloService;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerMethod;
 
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerMethod.class)
public class SampleTest {

    @Inject
    private HelloService helloService;

    @Configuration
    public Option[] config() {

        return options(
            mavenBundle("com.example.myproject", "myproject-api", "1.0.0-SNAPSHOT"),
            bundle("http://www.example.com/repository/foo-1.2.3.jar"),
            junitBundles()
            );
    }

    @Test
    public void getHelloService() {
        assertNotNull(helloService);
        assertEquals("Hello Pax!", helloService.getMessage());
    }
}

The @RunWith(PaxExam.class) annotation is enough to hook Pax Exam into JUnit and let it do its magic of setting up a test container with an OSGi framework and your bundles under test.

The @ExamReactorStrategy annotation determines whether or not the OSGi framework gets restarted for every single test. The value used here is actually the default (so you could drop this annotation), indicating that the framework will be restarted for each test to provide a maximum of isolation.

Configuring the Test Container

Any Pax Exam JUnit test requires a method annotated with @Configuration returning an array of configuration options. These options define the set of bundles to be provisioned to the OSGi framework in the test container. You create options by calling static methods coming from

import static org.ops4j.pax.exam.CoreOptions.*;

Pax Exam has a large number of Configuration Options, but for standard use cases, you only need to remember the five or six most important ones.

mavenBundle() lets you specify a bundle in terms of its Maven coordinates. It will be provisioned to the test container from a local or remote Maven repository using the standard Maven lookup and caching procedures.

Using bundle(), you can provision a bundle from any kind of URL supported by Pax URL. Of course, this includes the standard file: and http: protocols, and there are some specialized protocols for grouping bundles, scanning directories and other useful things.

junitBundles() provisions JUnit and its dependencies as OSGi bundles to the test container.

Test Methods

Any method annotated with @Test will be run as a test within the OSGi framework launched by the test container.

Pax Exam builds a bundle on the fly called test probe which contains your test class and installs this bundle in the test container.

Using Dependency Injection on fields marked with @javax.inject.Inject, the body of your test method can access the BundleContext of the probe bundle or any service obtained from the OSGi service registry.

The test probe contains a DynamicImport-Package: * manifest header, so your test methods work with any package exported by any bundle in your test container.

@Before or @After methods and @Rule fields work as expected. The corresponding methods are executed within the test container. (@BeforeClass, @AfterClass and @ClassRule are not supported at the moment.)

Maven Considerations

The example test above uses mavenBundle(). This feature of pax-exam is provided by pax-url-aether. It finds OSGi bundles as Maven artifacts by opening up a separate session with the Aether repository manager. This all works fine so long as the versions of the bundles that you want are sitting in your local repository or some other configured repository. It won't resolve artifacts from the reactor. It's not hard to achieve this: put your pax-exam tests in an integration test module, and always  run mvn install .

If this is not practical or desirable for you (e.g. you use a continuous integration system that won't do mvn install easily, or if you really want to embed a pax-exam test into the project that builds the bundle), there are other alternatives. One is Andreas Veithen's alta plugin. The explanation he wrote will put you on the road if you need to go here.

Next Steps

The test container and the OSGi framework used for running your tests are looked up from the classpath using java.util.ServiceLoader, so you have to make sure that the appropriate dependencies are on the classpath of your test project.

Read more about setting up the Maven Dependencies for running Pax Exam tests.