2011-08-23 49 views
11

我想弄清楚如何在OSGi中使用JUnit实现多束集成测试。在JUnit测试环境中使用OSGi声明式服务

有了集成测试,我的意思是实例化bundle的子集以自动验证该子系统中的功能。

我们运行Equinox并使用Eclipse作为工具链。 Eclipse提供了“Run as JUnit Plug-in”选项,它将OSGi框架引入并实例化了配置包,所以我想这是要遵循的路径,但我没有找到一种方法将DS引用注入到我的测试中。 我已经看到使用ServiceTracker作为访问不同服务包的程序化手段,但是这打败了DS的目的,不是吗?

我刚刚开始使用OSGI,所以我想我只是错过了一些让我将多捆绑测试放在一起的难题。

任何想法?

谢谢,杰拉德。

*编辑:解*

进一步寻找到这个问题后,我终于想通了如何使用JUnit插件功能,把这个MULT束集成测试到位:

对于要动态服务注入工作,必须创建一个服务定义文件,其中必须声明注入的依赖关系,因为它通常在使用DS时完成。 此文件通常位于OSGI-INF/目录下。例如OSGI-INF/service.xml

service.xml中,必须声明所需的依赖关系为这个测试,但不提供自己的服务:

service.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="MyTest" activate="startup" deactivate="shutdown"> 

    <implementation class="com.test.functionaltest.MyTester"/> 
    <reference name="OtherService" interface="com.product.service.FooService" policy="static" cardinality="1..1" bind="onServiceUp" unbind="onServiceDown"/> 

</scr:component> 

这将指示DS使用声明的onServiceUp方法注入上FooService接口的依赖。 onServiceDown必须在测试运行后的OSGi关闭阶段调用时调用。

com.test.functionaltest.MyTester包含要执行的测试方法,遵循典型的JUnit实践。

到这里,这一切都是'靠书'。但是,如果运行Junit,则在访问对FooService的引用时将抛出NullPointerException。原因在于OSGi框架与JUnit测试运行环境处于竞争状态,并且通常,Junit测试运行器赢得该比赛,在引入所需服务之前执行测试。

为了解决这种情况,需要使Junit测试等待OSGi运行时执行其工作。我通过使用CountDownLatch解决了这个问题,它初始化为测试中所需的相关服务的数量。 然后每个依赖注入方法都会倒计时,当它们全部完成时,测试将开始。代码如下所示:

private static CountDownLatch dependencyLatch = new CountDownLatch(1);// 1 = number of dependencies required  
static FooService fooService = null; 
public void onFooServiceUp(FooService service) { 
    fooService = service; 
    dependencyLatch.countDown(); 
} 

注意,fooService参考必须是静态的,允许共享的OSGi和JUnit执行上下文之间的服务引用。 CountDownLatch提供了一个高级别的同步机制来安全发布这个共享引用。

然后,依赖检查应在测试执行前加入:

@Before 
public void dependencyCheck() { 
    // Wait for OSGi dependencies 
    try { 
     dependencyLatch.await(10, TimeUnit.SECONDS); 
     // Dependencies fulfilled 
    } catch (InterruptedException ex) { 
     fail("OSGi dependencies unfulfilled"); 
    } 
} 

这样对OSGi的DS服务注入依赖JUnit框架等待或超时后失败。

我花了相当一段时间才完全弄清楚了这一点。我希望将来可以为同行程序员节省一些头痛的问题。

回答

6

*编辑:解*

进一步寻找到这个问题后,我终于想通了如何使用JUnit插件功能,把这个MULT束集成测试到位:

对于要动态服务注入工作,必须创建一个服务定义文件,其中必须声明注入的依赖关系,因为它通常在使用DS时完成。 此文件通常位于OSGI-INF/目录下。例如OSGI-INF/service.xml

service.xml中,必须声明所需的依赖关系为这个测试,但不提供自己的服务:

service.xml 
<?xml version="1.0" encoding="UTF-8"?> 
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="MyTest" activate="startup" deactivate="shutdown"> 

    <implementation class="com.test.functionaltest.MyTester"/> 
    <reference name="OtherService" interface="com.product.service.FooService" policy="static" cardinality="1..1" bind="onServiceUp" unbind="onServiceDown"/> 

</scr:component> 

这将指示DS使用声明的onServiceUp方法注入上FooService接口的依赖。 onServiceDown必须在测试运行后的OSGi关闭阶段调用时调用。

com.test.functionaltest.MyTester包含要执行的测试方法,遵循典型的JUnit实践。

到这里,这一切都是'靠书'。但是,如果运行Junit,则在访问对FooService的引用时将抛出NullPointerException。原因在于OSGi框架与JUnit测试运行环境处于竞争状态,并且通常,Junit测试运行器赢得该比赛,在引入所需服务之前执行测试。

为了解决这种情况,需要使Junit测试等待OSGi运行时执行其工作。我通过使用CountDownLatch解决了这个问题,它初始化为测试中所需的相关服务的数量。 然后每个依赖注入方法都会倒计时,当它们全部完成时,测试将开始。代码如下所示:

private static CountDownLatch dependencyLatch = new CountDownLatch(1);// 1 = number of dependencies required  
static FooService fooService = null; 
public void onFooServiceUp(FooService service) { 
    fooService = service; 
    dependencyLatch.countDown(); 
} 

注意,fooService参考必须是静态的,允许共享的OSGi和JUnit执行上下文之间的服务引用。 CountDownLatch提供了一个高级别的同步机制来安全发布这个共享引用。

然后,依赖检查应在测试执行前加入:

@Before 
public void dependencyCheck() { 
    // Wait for OSGi dependencies 
    try { 
     dependencyLatch.await(10, TimeUnit.SECONDS); 
     // Dependencies fulfilled 
    } catch (InterruptedException ex) { 
     fail("OSGi dependencies unfulfilled"); 
    } 
} 

这样对OSGi的DS服务注入依赖JUnit框架等待或超时后失败。

我花了相当一段时间才完全弄清楚了这一点。我希望将来可以为同行程序员节省一些头痛的问题。

1

我不熟悉你提到的Eclipse工具,但我们已经成功使用Pax Exam进行集成测试Apache Sling。如果您熟悉Maven,那么https://svn.apache.org/repos/asf/sling/trunk/installer/it/pom.xml上的POM可能会帮助您入门,并且https://github.com/tonit/Learn-PaxExam看起来也是一个很好的起点。

Sling testing tools也可以通过允许bundle在运行时向OSGi框架提供JUnit测试来帮助实现此目的,如果您的项目生成可用于测试的可运行JAR文件,这非常有用。

1

使用运行配置中的选项卡进行设置。所以右击,选择“运行方式”,选择“运行配置...”,双击“JUnit插件测试”,然后在插件选项卡上添加依赖关系 - 几乎与普通启动器相同

一些链接: http://publib.boulder.ibm.com/infocenter/ratdevz/v8r0/index.jsp?topic=/org.eclipse.pde.doc.user/guide/tools/launchers/junit_launcher.htmhttp://publib.boulder.ibm.com/infocenter/ratdevz/v8r0/index.jsp?topic=/org.eclipse.pde.doc.user/guide/tools/launchers/junit_main.htm

+0

当然,这是我提到的“作为JUnit插件运行”选项,但它似乎不足以让注入声明服务。即您在哪里声明绑定方法?还有关于上下文的问题。注入是否会在Junit环境中发生?如果是这样,该机制如何运作? – maasg

+0

与您在应用程序中使用DS相同。您需要添加DS捆绑软件,这会在加载其他捆绑软件时扫描其他软件包并管理布线。 DS绑定在bundle的jar中的OSGI-INF的组件xml中声明。有关在Eclipse插件项目中使用DS的解释,请参阅http://www.vogella.de/articles/OSGi/article.html#declarativeservices_run。 HTH – earcam

+0

感谢您的建议。在Junit插件运行的上下文中,DS注入方法没有被调用。我的OSG-INF正确地声明它们。 – maasg

1

我想这将是一个有点清洁,以获得org.apache.felix.scr.ScrService的保持积极等待组件变得活跃。该接口由equinox和felix实现。

Java docAPI Usage

-2

我认为在上面的解决方案中,CountDownLatch是没有必要的。

问题是DS Context中的JUnit为他自己实例化了一个JUnitTest类。第一个DS Context实例化您的JUnitTest类并为FooService调用绑定onFooServiceUp,但在此之后,JUnit实例化自己的JUnitTest类而不调用onFooServiceUp的绑定方法。在这种情况下,FooService在JUnitTest中不可用。

如果您将FooService声明为static(如您所做的那样)并在方法onFooServiceUp中分配,则不需要使用CountDownLatch进行构造。