OSGi Service Test Helper: ServiceRegistrationRule
OSGi Service Tests can be an efficient means to avoid problems related to dangling service references. As promised in my post about writing simple service contribution verifications, this time I introduce a JUnit rule that assists in testing interactions between components.
OSGi Service Tests for Component Interaction
Assume we have a service that notifies related observers bound according to the whiteboard-pattern. Precisely we have a Service
declaration and ServiceImpl
as in the previous post. Additionaly we support ServiceListener
s that should be notified on particular actions.
To represent such an action we broaden the service interface of our example with a method declaration called Service#execute()
:
public interface Service { void execute(); }
Beside the implementation of this execute
method the contribution class have to provide the capabilities to bind and unbind ServiceListener
references:
public class ServiceImpl implements Service { public void execute() { [...] } public void bind( ServiceListener listener ) { [...] } public void unbind( ServiceListener listener ) { [...] } }
As notification destination the callback type ServiceListener
s provides a method declaration called ServiceListener#executed()
:
public interface ServiceListener { void executed(); }
To complete the setup we have to register the service component, which we do again via declarative services. Note the additional 0..n reference declaration:
<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="Implementation of Service API"> <implementation class="com.codeaffine.example.core.ServiceImpl"/> <service< <provide interface="com.codeaffine.example.api.Service"/> </service> <reference bind="bind" unbind="unbind" cardinality="0..n" interface="com.codeaffine.example.api.ServiceListener" name="ServiceListener" policy="dynamic" /> </scr:component>
Now the question is: How can we test that un-/binding of a listener works correctly and notifications are dispatched as expected? The basic idea is to register a ServiceListener
spy and trigger Service#execute
on the actual service implementation.
The spy records calls to execute
and allows to verify that binding and notification work as expected. Once we have ensured this, we can go on and deregister a primarily registered spy and verify that it is not notified about a subsequent action event. This makes sure unbinding works also as planned.
However the test fixture for this scenario usually needs a bit of OSGi boilerplate. To reduce the clutter I have written a little JUnit rule that eases service registration and automatically performs a service registry cleanup after each test run.
ServiceRegistrationRule
As every other JUnit TestRule
the ServiceRegistrationRule
has to be provided as a public field in our PDE test. Note how the rule uses a parameterized constructor given the class instance of the test case. This reference is used to get hold of an appropriate BundleContext
for service de-/registration.
@Rule public final ServiceRegistrationRule serviceRegistration = new ServiceRegistrationRule( getClass() ); private ServiceListener listener; private Service service; @Before public void setUp() { service = collectServices( Service.class, ServiceImpl.class ).get( 0 ); listener = mock( ServiceListener.class ); }
The implicit test setup retrieves the registered service under test using the ServiceCollector
I introduced in the last post. The listener DOC is created as spy using mockito. The first test scenario described above looks like this:
@Test public void executeNotification() { serviceRegistration.register( ServiceListener.class, listener ); service.execute(); verify( listener ).executed(); }
Pretty straight forward, isn’t it?
Note that the ServiceRegistrationRule
takes care of cleanup and removes the spy service from the service registry. To facilitate a test for the unbind scenario, the rule’s register
method returns a handle to the service registration:
@Test public void executeAfterListenerRemoval() { Registration registration = serviceRegistration.register( ServiceListener.class, listener ); registration.unregister(); service.execute(); verify( listener, never() ).executed(); }
Line five (registration.unregister()
) removes the listener spy from the service registry. This triggers an unbind and the listener gets never invoked. Of course a real world scenario could add additional tests for multiple listener registrations, exception handling and the like, but I think the concept has been made clear.
Conclusion
So far the ServiceRegistrationRule
proves itself quite useful in our current project. It reduces boilerplate significantly and makes the tests cleaner and increases readability. The class is part of the com.codeaffine.osgi.test.util feature of the Xiliary P2 repository: http://fappel.github.io/xiliary
In case you want to have a look at the code or file an issue you might also have a look at the Xiliary GitHub project: https://github.com/fappel/xiliary
For everything else feel free to use the commenting section below. In a followup I will explain how to setup a maven-tycho build with integrated PDE-Tests as those described above. This is somewhat tricky as tycho does not allow to access the bundles built by the current reactor, so stay tuned..
Reference: | OSGi Service Test Helper: ServiceRegistrationRule from our JCG partner Rudiger Herrmann at the Code Affine blog. |