Get in the Classroom with Pax Exam for OSGi Integration Testing

Posted: September 22nd, 2014 | Author: | Filed under: Technology | Tags: , , | No Comments »

As anyone who has written integration tests previously can attest, creating the testing environment is typically tougher than writing the tests themselves. This includes determining the correct dependencies to support both the testing framework and environment. In some cases, one would need to add as many as 10 to 20 dependencies just to perform the most simplistic integration test. Fortunately, the Fuse platform provides a single artifact called pax-exam-karaf which includes all of the required base Pax Exam 2.6.0 framework (the version supported for the Fuse 6.1 platform) dependencies.

In the sample template project, the Pax Exam test is called RouteTest and located the <PROJECT-ROOT>/src/test/com/redhat/fuse folder. Since we want to replicate the existing camel testing functionality as was accomplished in the original unit test, the Pax Exam test will extend the CamelTestSupport class to tap into the testing capabilities found within the Camel testing framework. This is in contrast to the CamelBlueprintTestSupport from the original test which is no longer needed since our testing environment is delegated to the Karaf container instead of having to spin up a Blueprint environment of our own. To configure the testing environment in the unit test, a method annotated with the @Configuration annotation is used.

	@Configuration
    public static Option[] configure() throws Exception {
        return new Option[] {

        		// define container
                karafDistributionConfiguration()
                .frameworkUrl(
                        maven().groupId("org.apache.karaf").artifactId("apache-karaf").type("zip")
                                .versionAsInProject()).useDeployFolder(false).karafVersion("2.3.0")
                        .unpackDirectory(new File("target/karaf/")),

                // container logging level
                logLevel(LogLevel.INFO),

                // add spring feature (required by camel-test)
                scanFeatures(
                        maven().groupId("org.apache.karaf.assemblies.features").artifactId("spring").type("xml")
                                .classifier("features").versionAsInProject(), "spring"),

                // add camel features
                scanFeatures(
                        maven().groupId("org.apache.camel.karaf").artifactId("apache-camel").type("xml")
                                .classifier("features").versionAsInProject(), "camel-blueprint","camel-test"),

                // build and start custom bundle                
            	streamBundle(bundle().add(Hello.class).add(HelloBean.class)
            			.add("OSGI-INF/blueprint/blueprint.xml",
    							new File(
    									"src/main/resources/OSGI-INF/blueprint/blueprint.xml")
    									.toURI().toURL())
    					.add("log4j.properties",
    							new File(
    									"src/main/resources/log4j.properties")
    									.toURI().toURL())			
						.set(Constants.BUNDLE_SYMBOLICNAME,
    										"com.redhat.fuse.test.fuse.camel.blueprint.test")
						.set(Constants.IMPORT_PACKAGE, "*")
						.build()).start()
        };
    }

First, the testing container itself is defined by using the static method karafDistributionConfiguration bring in Karaf from a Maven as a zip dependency and to unpack into the project’s target folder. Next, Fuse features that our Blueprint camel project would depend on are added to the container. This is accomplished through the scanFeatures static method. Even though this particular method is marked as deprecated, it is still the recommended approach to adding features in later versions of Pax Exam 2. Pax Exam version 3 does have a fully supported features method which does provide similar functionality. Camel blueprint and testing features along with a dependency feature on Spring is added to the container. The final task in the Configuration method is to build an OSGi bundle containing the blueprint example project itself. This is accomplished using the streamBundle static method which allows the developer to define classes, resources and OSGi configurations options that will be added to the bundle and deployed to the container.

With the testing container now prepared, we will perform an additional configuration on the Camel testing framework and validate the container is successfully provisioned prior to running the test method. The isCreateCamelContextPerClass method is overridden to return true to ensure only one camel context is created for the test class regardless the number of test methods. Finally, checks are made to ensure both the camel context and features defined in the Configuration method have started and are available in the testing container.

    @Override
    public boolean isCreateCamelContextPerClass() {
        // we override this method and return true, to tell Camel test-kit that
        // it should only create CamelContext once (per class), so we will
        // re-use the CamelContext between each test method in this class
        return true;
    }

    @Before
    public void testSetup() throws Exception {
        // Assert Features Installed
    	assertTrue(featuresService.isInstalled(featuresService.getFeature("spring")));    
    	assertTrue(featuresService.isInstalled(featuresService.getFeature("camel-core")));
        assertTrue(featuresService.isInstalled(featuresService.getFeature("camel-blueprint")));
    }

    @Override
    protected void doPreSetup() throws Exception {
        assertNotNull(camelContext);
    }

Let’s now go ahead and replicate the functionality of the unit test from the origin project by validating a camel message is sent through the route. For the most part, the test is identical with few modifications on how the Camel endpoints are injected into the test. The test can be run through Eclipse or by building the project using Maven as follows which will run all of the tests configured in the project:

mvn clean install

Whichever method chosen to run the unit test, you should be able to see the Karaf container startup, call the test method, and validate the testing assertions.

Even in a straightforward integration test such as this, the number of dependencies, configurations and options required spotlight the need for template project such as this to eliminate the amount of boilerplate code required by the developer. Integration testing should not be avoided due to the additional complexities, but it is the hope that providing template projects such as this allow for developer adoption toward additional testing strategies. In an upcoming post, we will discuss how Arquilian can be used to perform integration testing of OSGi applications.



Leave a Reply