13

I'm currently thinking of "How to design an OSGi component so that it's easy to write tests for it with frameworks like jUnit and Mockito".

Mocking inter-bundle-dependencies is quite easy since OSGi strengthens the DIP (Dependency Inversion Principle) and injector-methods (e.g. setter) usually exist.
But what about bundle internal dependencies?

For example look at this case. Now I want to bring it into an OSGi context... Image we want to provide any kind of network protocol as a declarative service in an OSGi platform and want to write unit-tests for testing the lower networking code which is directly interacting with the socket object.

If we would refactor the socket creation into a separate but still bundle internal POJO (Plain Old Java Object) class, how should we inject it into the protocol implementation?

  • In the unit test we could simply use a setter method but who would do this in the OSGi container for us?
  • Subclassing the tested class and overwriting a creator-method would only work if the tested class is not declared as final.
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Marc-Christian Schulze
  • 3,154
  • 3
  • 35
  • 45

1 Answers1

8

Testing the interaction with the OSGi container is, strictly speaking, an integration test. For this you could use Pax Exam, it's a bit fiddly to get the hang of, but works really well (especially if you're using maven and/or karaf features).

Additionally you can use TinyBundles which can dynamically create deployable bundles/fragments from within your test (very cool) to mock out other bundles/fragments to ensure inter-bundle integration without bring up a full environment.

For unit or small scale integration testing (i.e. without the container) then you can just mock out the BundleContext (or if using DS the ComponentContext as well) should you need it.

I'm a little unclear about your questions in the bullet points. If there's an internal POJO then it'd be your responsibility to wire up dependencies via setters, otherwise if it exposed to the OSGi service registry then dependencies are resolved by the framework (either DS or ServiceTracker).

Also subclassing something to overwrite a creator-method means you're no longer testing the original class - this is a code smell - try refactoring it to pass in the creator code as a separate class (constructor or setter), then this new creator code (socket creation) can be tested independently (with no consideration for OSGi or even the protocol class where it will be used).

earcam
  • 6,662
  • 4
  • 37
  • 57
  • 1
    If I use POJOs within an OSGi component running in an OSGi container, the component itself has to create an instance of the POJOs e.g. in the constrcutor code since the container isn't able to inject it. How should I intercept this process in a Unit-Test? Of course I could create a setter method that will overwrite the previously created POJO within the component but this also has some code smell. "Do we really test the code of the component like it would run in the container?" Some side effects like calling the POJO in the constructor code might happen and break my tests. – Marc-Christian Schulze Aug 10 '11 at 19:32
  • I think I follow, you have a Pojo creating other pojos inside it's constructor (or as initialized fields), e.g. a Protocol class creating Sockets? If this is the case then you need to separate the creation of the Socket from your Protocol class - by passing as a constructor arg or applying with a setter (but removing any creation of Sockets from within the Protocol class). This way you pass in a mocked Socket for unit testing, whereas for your bundle create a SocketServiceFactory returning real Sockets (production) and for container tests mock out this service to return mock Sockets – earcam Aug 11 '11 at 12:30
  • Perfect. Using a transparent ServiceFactory is excatly what I was looking for. Previously I thought ServiceFactories and ComponentFactories were the same. ;) – Marc-Christian Schulze Aug 11 '11 at 16:46
  • Cool, good luck & happy coding =) (would you mind up-voting the answer too, thanks) – earcam Aug 11 '11 at 18:21
  • Ok I still got one more question. ;) I'm currently registering the service factory using BundleContext.registerService. Is there the possibility to let the framework inject other services in the instances created by my own factory? – Marc-Christian Schulze Aug 15 '11 at 10:45
  • I've added an answer to http://stackoverflow.com/questions/7033222/osgi-using-servicefactories/7080234 hope that's what you're looking for. AFAIK there's no way for DS intercept and inject those services created from a ServiceFactory, but as DS creates ComponentFactory instances it's is capable of managing dependencies in the services these create. – earcam Aug 16 '11 at 14:57