JBoss.orgCommunity Documentation

Chapter 6. Husky Test Framework

6.1. Overview
6.2. Architecture
6.3. Configuration
6.4. Writing Husky Tests

JBoss OSGi Husky is a OSGi Test Framework that allows you to run plain JUnit4 test cases from within an OSGi Framework. That the test is actually executed in the the OSGi Framework is transparent to your test case. There is no requirement to extend a specific base class nor do you need a special test runner. Your OSGi tests execute along side with all your other (non OSGi specific) test cases in Maven, Ant, or Eclipse.

Some time ago I was looking for ways to test bundles that are deployed to a remote instance of the JBoss OSGi Runtime. I wanted the solution to also work with an OSGi Framework that is bootstrapped from within a JUnit test case.

The basic problem is of course that you cannot access the artefacts that you deploy in a bundle directly from your test case, because they are loaded from different classloaders.

Former releases of JBoss OSGi used an approach which is documented in Non intrusive OSGi Bundle Testing and is still available. Although the remote logging approach worked for simple scenarios, it does not allow for fine grained interaction with the OSGi Framework (i.e. access to the registry). An additional problem was the asynchronous nature of LogEntry delivery.

For this release however, I revisited the problem and added a few more requirements.

  • Test cases SHOULD be plain JUnit4 POJOs
  • There SHOULD be no requirement to extend a specific test base class
  • There MUST be no requirement on a specific test runner (i.e. MUST run with Maven)
  • There SHOULD be a minimum test framework leakage into the test case
  • The test framework MUST support embedded and remote OSGi runtimes with no change required to the test
  • The same test case MUST be executable from outside as well as from within the OSGi Framework
  • There SHOULD be a pluggable communication layer from the test runner to the OSGi Framework
  • The test framework MUST NOT depend on OSGi Framework specific features
  • There MUST be no automated creation of test bundles required by the test framework

The next section explains the solution that now comes as JBoss OSGi Husky

JBoss OSGi Husky has client side interceptor that fields the test request to an embedded/remote OSGi Framework where the test case is then actually executed.

Here is how it works

  1. A Bridge intercepts a test and determines the FQN of the test case and the test method from the call stack. It then delegates the execution to the same (or another) test in and isolated test environment. An isolated test environment is one that does not have the same class loading space as the test itself.
  2. A Bridge is associated with an Invoker. Invokers may be arbitarily complex. Local 'in proccess' invokers are possible just as well as remote invokers.
  3. The Invoker sends the Request to a Connector in the isolated test environment.
  4. A Connector has associated PackageListeners that are responsible for processing test cases for their respective test packages.
  5. A PackageListeners delegates the Request to a test Runner, typicaly this would be a JUnitRunner.
  6. The Runner injects the Context into the test case and returns a Response, which the Connector returns to the Invoker.
  7. The Bridge finally translates potential Failures that may be contained in the Result, to test failures on the client side.

The JBoss OSGi jboss-osgi-husky.jar bundle registers the Connectors. The JMXConnector is always registered. The SocketConnector is registered when the appropriate configuration options are set. It then registers the HuskyExtender, which is a BundleListener that inspects every incomming bundle for the Test-Package manifest header. The Extender creates a PackageListener for every package in the 'Test-Package' manifest header and registers them with the available Connectors.

In the target OSGi Framework, which is the one that has the jboss-osgi-husky.jar bundle installed, you set these properties

KeyValueDescription
org.jboss.osgi.husky.runtime.connector.host localhost The Husky socket connector host poperty
org.jboss.osgi.husky.runtime.connector.port 5401 The Husky socket connector port poperty

Both properties must be set for the SocketConnector to be available.

On the client side, you must configure the Invoker you want to use.

KeyValueDescription
org.jboss.osgi.husky.Invoker org.jboss.osgi.husky.internal.OSGiInvoker The client side Husky Invoker

This particular invoker will also look for the 'org.jboss.osgi.husky.runtime.connector.host' and 'org.jboss.osgi.husky.runtime.connector.port' properties and if available will use a socket invocation.

In a typical Husky test you have

For OSGi, the descriminator would be the BundleContext that gets injected by the 'in container' test Runner

The interceptor would be a call to one of the Bridge.run() variants.



public class SimpleHuskyTestCase
{
   @ProvideContext
   public BundleContext context;
   ...
   @Test
   public void testSimpleBundle() throws Exception
   {
      // Tell Husky to run this test method within the OSGi Runtime
      if (context == null)
         BridgeFactory.getBridge().run();
      
      // Stop here if the context is not injected
      assumeNotNull(context);
      
      // Get the SimpleService reference
      ServiceReference sref = context.getServiceReference(SimpleService.class.getName());
      assertNotNull("SimpleService Not Null", sref);
      
      // Access the SimpleService 
      SimpleService service = (SimpleService)context.getService(sref);
      assertEquals("hello", service.echo("hello"));
   }
}
    

The bundle that contains the test case must have the Test-Package manifest header configured. Here is the aQute Bnd Tool configuration for doing so.

Bundle-SymbolicName: example-simple-husky

Bundle-Activator: org.jboss.test.osgi.example.simple.bundle.SimpleActivator

Private-Package: org.jboss.test.osgi.example.simple.bundle

# Export the package that contains tthe test case
Export-Package: org.jboss.test.osgi.example.simple

# Tell Husky that there are test cases in this package
Test-Package: org.jboss.test.osgi.example.simple