13

我试图弄清楚如何使用 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();
}

请注意,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 服务注入依赖项,否则会在超时后失败。

我花了很长时间才完全弄清楚这一点。我希望它可以为将来的程序员同行省去一些麻烦。

4

5 回答 5

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>

这将指示 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 服务注入依赖项,否则会在超时后失败。

我花了很长时间才完全弄清楚这一点。我希望它可以为将来的程序员同行省去一些麻烦。

于 2012-06-12T16:51:19.230 回答
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 测试工具还可以通过允许包在运行时将 JUnit 测试贡献给 OSGi 框架来帮助解决此问题,如果您的项目生成可用于测试的可运行 jar,这将非常有用。

于 2011-08-24T07:54:19.947 回答
1

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

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

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

于 2011-08-24T07:55:59.053 回答
1

我想获得 org.apache.felix.scr.ScrService 并积极等待组件变为 ACTIVE 会更干净一些。此接口由 Equinox 和 felix 实现。

Java 文档API 使用

于 2011-11-08T13:32:32.043 回答
-2

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

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

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

于 2017-01-10T14:42:48.733 回答