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

maa*_*asg 12 java junit osgi declarative-services

我正在试图弄清楚如何使用JUnit在OSGi中实现多束集成测试.

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

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

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

有任何想法吗?

谢谢,杰拉德.

*编辑:解决方案*

在深入研究这个问题之后,我终于想出了如何使用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>
Run Code Online (Sandbox Code Playgroud)

这将指示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();
}
Run Code Online (Sandbox Code Playgroud)

请注意,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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

maa*_*asg 7

*编辑:解决方案*

在深入研究这个问题之后,我终于想出了如何使用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>
Run Code Online (Sandbox Code Playgroud)

这将指示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();
}
Run Code Online (Sandbox Code Playgroud)

请注意,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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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