在JUnit测试的上下文中使用OSGi声明性服务

时间:2011-08-23 12:55:52

标签: java junit osgi declarative-services

我正在尝试弄清楚如何使用JUnit在OSGi中实现多包集成测试。

使用集成测试,我的意思是实例化一个子集,以自动验证该子系统中的功能。

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

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

有什么想法吗?

谢谢Gerard。

*编辑:解决方案*

在深入研究这个问题后,我终于想出了如何使用JUnit插件功能实现这种多包集成测试:

要使动态服务注入工作,必须创建一个服务定义文件,其中必须声明注入的依赖项,因为它通常在使用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");
    }
}

这样Junit框架会等待OSGi DS服务注入依赖项,或者在超时后失败。

我花了很长时间才完全弄明白这一点。我希望将来可以为其他程序员带来一些麻烦。

5 个答案:

答案 0 :(得分:6)

*编辑:解决方案*

在深入研究这个问题后,我终于想出了如何使用JUnit插件功能实现这种多包集成测试:

要使动态服务注入工作,必须创建一个服务定义文件,其中必须声明注入的依赖项,因为它通常在使用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");
    }
}

这样Junit框架会等待OSGi DS服务注入依赖项,或者在超时后失败。

我花了很长时间才完全弄明白这一点。我希望将来可以为其他程序员带来一些麻烦。

答案 1 :(得分: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,这将非常有用。

答案 2 :(得分:1)

您可以使用运行配置中的选项卡进行设置。

右键单击,选择“运行方式”,选择“运行配置...”,双击“JUnit Plug-in Test”,然后在插件选项卡上添加依赖项 - 与普通启动器几乎相同< / p>

一些链接: 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

答案 3 :(得分:1)

我想抓住org.apache.felix.scr.ScrService并主动等待组件变为ACTIVE会更加清晰。此接口由equinox和felix实现。

Java docAPI Usage

答案 4 :(得分:-2)

我认为在上述解决方案中,CountDownLatch不是必需的。

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

如果将FooService声明为静态(如您所做)并在方法onFooServiceUp中赋值,则不需要使用CountDownLatch进行构造。