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

2022-09-04 22:36:41

我试图弄清楚如何使用JUnit在OSGi中实现多捆绑集成测试。

对于集成测试,我的意思是实例化捆绑包的子集以自动验证该子系统中的功能。

我们正在运行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>

这将指示 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();
}

请注意,引用必须是静态的,以允许在 OSGi 和 JUnit 执行上下文之间共享服务引用。CountDownLatch 为安全发布此共享引用提供了一种高级同步机制。fooService

然后,应在测试执行之前添加依赖关系检查:

@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

* 编辑 : 解决方案 *

在进一步研究了这个问题之后,我终于弄清楚了如何使用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();
}

请注意,引用必须是静态的,以允许在 OSGi 和 JUnit 执行上下文之间共享服务引用。CountDownLatch 为安全发布此共享引用提供了一种高级同步机制。fooService

然后,应在测试执行之前添加依赖关系检查:

@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 服务注入依赖项或在超时后失败。

我花了很长时间才完全弄清楚这个问题。我希望它能为将来的程序员同伴们带来一些麻烦。


答案 2

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

在这种情况下,Sling 测试工具还可以提供帮助,允许捆绑软件在运行时将 JUnit 测试贡献给 OSGi 框架,如果您的项目生成了可用于测试的可运行 jar,这将非常有用。


推荐