Hopefully, you’re in the habit of writing unit tests for your code. If so, that practice should extend to the code that is contained within your OSGi bundles. Because Spring-DM encourages POJO-based OSGi development, you can continue to write unit-tests for the classes that define and consume OSGi services just like you would for any other non-OSGi code.
But it’s also important to write tests that exercise your OSGi services as they’ll be used when deployed in an OSGi container. To accommodate in-OSGi integration testing of bundles, Spring-DM provides AbstractConfigurableBundleCreatorTests, a JUnit 3 base test class from which you can write your bundle tests.
What’s fascinating is how tests based on AbstractConfigurableBundleCreatorTests work. When the test is run, it starts up an OSGi framework implementation (Equinox by default) and installs one or more bundles into the framework. Finally, it wraps itself in an on-the-fly bundle and installs itself into the OSGi framework so that it can test bundles as an insider.
Writing a Basic OSGi Test
To illustrate, let’s write a simple test that loads our Translator interface bundle and the Pig Latin implementation bundle:
package com.habuma.translator.test;
import org.osgi.framework.ServiceReference;
import org.springframework.osgi.test.
AbstractConfigurableBundleCreatorTests;
import com.habuma.translator.Translator;
public class PigLatinTranslatorBundleTest
extends AbstractConfigurableBundleCreatorTests {
@Override
protected String[] getTestBundlesNames() {
return new String[] {
“com.habuma.translator, interface, 1.0.0”,
“com.habuma.translator, pig-latin, 1.0.0”
};
}
public void testOsgiPlatformStarts() {
assertNotNull(bundleContext);
}
}
The getTestBundleNames() method returns an array of Strings where each entry represents a bundle that should be installed into the OSGi framework for the test. The format of each entry is a comma-separated set of values that identify the bundle by its Maven group ID, artifact ID, and version number.
So far, our test has a single test method, testOsgiPlatformStarts(). All this method does is test that the OSGi framework has started by asserting that bundleContext (inherited from AbstractConfigurableBundleCreatorTests) is not null.
Testing OSGi Service References
A more interesting test we could write is one that uses the bundle context to lookup a reference to the Translator service and assert that it meets our expectations:
public void testServiceReferenceExists() {
ServiceReference serviceReference =
bundleContext.getServiceReference(Translator.class.
getName());
assertNotNull(serviceReference);
assertEquals(“Pig Latin”,
serviceReference.getProperty(“translator.language”));
}
Here we retrieve a ServiceReference from the bundle context and assert that it isn’t null. This means that some implementation of the Translator service has been published in the OSGi service registry. Then, it examines the properties of the service reference and asserts that the “translator.language” property has been set to “Pig Latin”, as we’d expect from how we published the service earlier.
Testing OSGi Services
One more thing we could test is that the Translator service does what we’d expect it to do. Certainly, this kind of test usually belongs in a unit test. But it’s still good to throw a smoke test its way to make sure that we’re getting the service we’re expecting.
We could use the ServiceReference to lookup the service. But, we can avoid any additional work with the OSGi API by having the Translator service wired directly into our test class. First, let’s add a Translator property and setter method to our test class
private Translator translator;
public void setTranslator(Translator translator) {
this.translator = translator;
}
When the test is run, Spring will attempt to autowire the property with a bean from its own Spring application context. But we haven’t defined a Spring application context for the test yet. Let’s do that now:
<?xml version=”1.0” encoding=”UTF-8”?>
<beans:beans xmlns:beans=”http://www.springframework.org/schema/
beans”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns=”http://www.springframework.org/schema/osgi”
xsi:schemaLocation=”http://www.springframework.org/schema/
beans
http://www.springframework.org/schema/beans/spring-beans.
xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd”>
<reference id=”translator”
interface=”com.habuma.translator.Translator” />
</beans:beans>
You’ll recognize that this Spring context definition looks a lot like the one we created for the service consumer. In fact, our test class will ultimately be a consumer of the Translator service. We just have one more thing to do before we can test the service—we’ll need to override the getConfigLocations() method to tell the test where it can find the context definition file:
@Override
protected String[] getConfigLocations() {
return new String[] { “bundle-test-context.xml” };
}
Now we can write our test method:
public void testTranslatorService() {
assertNotNull(translator);
assertEquals(“id-DAY is-thAY ork-wAY”,
translator.translate(“Did this work”));
}
This method assumes that by the time it is invoked, the translator property has been set. It first asserts that it is not null and then throws a simple test String at it to test that the service does what we expect.
Changing the Tested OSGi Framework
By default, Spring-DM tests are run within Equinox. But you can change them to run within another OSGi framework implementation such as Apache Felix or Knopflerfish by overriding the getConfigLocations() method. For example, to run the test within Apache Felix:
@Override
protected String getPlatformName() {
return Platforms.FELIX;
}
Or for Knoplerfish:
@Override
protected String getPlatformName() {
return Platforms.KNOPFLERFISH;
}
Providing a Custom Manifest
When a Spring-DM test gets wrapped up in an on-the-fly bundle, a manifest will be automatically generated for it. But if you’d like to provide a custom manifest. To provide a custom manifest for the on-the-fly bundle, all you need to do is override the getManifestLocation(). For example:
protected String getManifestLocation() {
return “classpath:com.habuma.translator.Translator.MF”;
}
Be aware, however, that if you provide a custom manifest, you must include a few things in that manifest to make Spring-DM testing work. First, you’ll need to specify a bundle activator:
Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
And you’ll need to import JUnit and Spring-DM packages:
Import-Package: junit.framework,
org.osgi.framework,
org.apache.commons.logging,
org.springframework.util,
org.springframework.osgi.service,
org.springframework.osgi.util,
org.springframework.osgi.test,
org.springframework.context
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}