This artifact provides support classes for OSGi testing with JUnit 4 including JUnit 4 Rules.
There are a number of operations that can be performed with or on the OSGi BundleContext
. However, this is low level API risk exposing tests to side effects resulting from these operations. Managing these side effects can results in large amounts of boiler plate code.
The BundleContextRule
is designed to help in these scenarios by giving access to an instance of the BundleContext that is context aware and results in the necessary cleanup when a test ends. The following cleanup is performed at the end of each test:
- bundles installed with
installBundle
are uninstalled - services obtained from
getService
are returned ServiceObjects
instances obtained fromgetServiceObjects
are returned- services registered by
registerService
are unregistered BundleListener
s registered withaddBundleListener
are removedFrameworkListener
s registered withaddFrameworkListener
are removedServiceListener
s registered withaddServiceListener
are removed (this includes closingServiceTracker
s created using theBundleContext
)
The BundleContextRule
is applied programmatically using a public field of the test class annotated with the JUnit4 @Rule
annotation.
@Rule
public BundleContextRule BundleContextRule = new BundleContextRule();
Now that the rule is in place, a BundleContext
instance can be injected into a non-private, non-static field annotated with @InjectBundleContext
@InjectBundleContext
BundleContext bundleContext;
In OSGi testing there are many scenarios that require installing pre-built bundles. The Bnd tool has support for easily building and embedding bundles within bundles. As a matter of convenience the BundleInstaller
utility was designed to simplify the task of finding and installing such embedded bundles.
The BundleInstaller
utility provides three convenience methods
Bundle installBundle(String pathToEmbeddedJar)
Bundle installBundle(String pathToEmbeddedJar, boolean startBundle)
BundleContext getBundleContext()
to simplify these use cases. The installBundle
methods use the findEntries
method from the Bundle API to locate embedded bundles.
An instance of this utility can be injected much like the BundleContext
using the @InjectBundleInstaller
.
@InjectBundleInstaller
BundleInstaller bundleInstaller;
Testing OSGi services can prove to be tricky business involving a lot of state management.
The ServiceRule
was designed to help alleviate this complexity by offering a mechanism to declare which service or set of services to use and how they should behave within the context of a test and then to clean up when the test ends.
The following criteria can be defined for any service or set of services:
- a service type for the service
- a cardinality declaring the minimum number of required services
- a filter expression to match available services
- a timeout within which a service or set of services must arrive before the test fails
The ServiceRule
can be applied programmatically using a public field of the test class annotated with the JUnit4 @Rule
annotation.
@Rule
public ServiceRule serviceRule = new ServiceRule();
With the rule in place, service instances can be injected into a non-private, non-static field annotated with @InjectService
@InjectService
LogService logService;
If the type of the field is of type java.util.List<S>
then the value will be a list of services of type S
where S
must not be a generic type.
@InjectService
List<LogService> logServices;
The list of services is provided in natural order of their service references. Tests are free to manipulate the list.
The type org.osgi.test.common.service.ServiceAware
provides several introspection methods related to tracking services.
If the type of the field is of type org.osgi.test.common.service.ServiceAware<S>
then the value will be an instance of that type where S
is the service type and S
must not be a generic type.
@InjectService
ServiceAware<LogService> lsServiceAware;
The instance is live and will reflect the state of tracked services and the underlying service tracker. This allows for cases having zero cardinality and/or to dynamically register services in the test itself and observe them with the ServiceAware
instance.
The cardinality can be specified to indicate a minimum number of expected services using the @InjectService.cardinality
property:
@InjectService(cardinality = 2)
// Gets the first service in ranking order (although it still waits for 2)
LogService logService;
@InjectService(cardinality = 2)
// The more likely usage
List<LogService> logService;
@InjectService(cardinality = 2)
ServiceAware<LogService> lsServiceAware;
The default cardinality is 1
.
Setting the cardinality to 0
allows for the test to continue immediately without waiting. This usage is most interesting used in conjunction with ServiceAware
fields which allows for a live view of the underlying tracker essentially giving a managed service tracker to use in tests. It must be noted that field types other than ServiceAware
are very likely to be null
or empty since potentially no value was available to populate them.
@InjectBundleContext
BundleContext bundleContext;
@InjectService(cardinality = 0)
ServiceAware<LogService> lsServiceAware;
@Test
public void testWithLogServices() {
bundleContext.registerService(LogService.class, new MyLogService(), null);
assertFalse(lsServiceAware.isEmpty());
}
Services can be filtered by declaring a filter expression using the @InjectService.filter
property.
@InjectService(filter = "(service.vendor=Acme Inc.)")
LogService logService;
As a matter of convenience the @InjectService.filterArguments
property can be used to provide format arguments where the @InjectService.filter
property uses format syntax.
@InjectService(
filter = "(%s=%s)",
filterArguments = {
Constants.SERVICE_VENDOR,
MyConstants.COMPANY_NAME
}
)
List<LogService> logServices;
A timeout can be applied in order to constrain the length of time the extension will wait before declaring a test failure. The timeout can be specified using the @InjectService.timeout
property.
@InjectService(timeout = 1000)
LogService logService;
The timeout will be cut short and the test will proceed when all other constraints of the @InjectService
are satisfied. As such, if all constraints can be immediately satisfied no waiting will occur.
The default timeout is 200
milliseconds.