在 JUnit 测试的上下文中使用 OSGi 声明式服务
Posted
技术标签:
【中文标题】在 JUnit 测试的上下文中使用 OSGi 声明式服务【英文标题】:Using OSGi declarative services in the context of a JUnit test 【发布时间】:2011-11-01 22:40:32 【问题描述】:我试图弄清楚如何使用 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服务注入依赖或者超时后失败。
我花了很长时间才完全弄清楚这一点。我希望它可以为以后的程序员同行省去一些麻烦。
【问题讨论】:
【参考方案1】:我认为在上述解决方案中 CountDownLatch 是不必要的。
问题在于 DS Context 中的 JUnit 为自己的每个实例化了一个 JUnitTest 类。首先 DS Context 实例化您的 JUnitTest 类并为 FooService 调用绑定 onFooServiceUp,但在此之后 JUnit 实例化自己的 JUnitTest 类而不调用绑定方法 onFooServiceUp。在这种情况下,FooService 在 JUnitTest 中不可用。
如果您将 FooService 声明为静态(如您所做的那样)并在 onFooServiceUp 方法中分配,则不需要使用 CountDownLatch 进行构造。
【讨论】:
【参考方案2】:* 编辑:解决方案 *
在进一步研究了这个问题之后,我终于想出了如何使用 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服务注入依赖或者超时后失败。
我花了很长时间才完全弄清楚这一点。我希望它可以为以后的程序员同行省去一些麻烦。
【讨论】:
【参考方案3】:我想获取 org.apache.felix.scr.ScrService 并积极等待组件变为 ACTIVE 会更干净一些。此接口由 Equinox 和 felix 实现。
Java doc 和 API Usage。
【讨论】:
【参考方案4】:您可以使用运行配置中的选项卡进行设置。
所以右键单击,选择“运行方式”,选择“运行配置...”,双击“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
【讨论】:
当然,这就是我提到的“作为 JUnit 插件运行”选项,但它似乎不足以注入声明式服务。即你在哪里声明绑定方法?还有关于上下文的问题。注入会发生在 Junit 上下文中吗?如果是这样,该机制是如何工作的? 和你在应用程序中使用DS一样。您需要添加 DS 捆绑包,这会在加载其他捆绑包时扫描它们并管理布线。 DS 绑定在捆绑包 jar 中的 OSGI-INF 中的组件 xml 中声明。有关在 Eclipse 插件项目中使用 DS 的说明,请参阅 vogella.de/articles/OSGi/article.html#declarativeservices_run。 HTH 感谢您的建议。 DS 注入方法不会在 Junit-plugin 运行的上下文中被调用。我的 OSG-INF 正确地声明了它们。 感谢您的建议。它需要的不仅仅是配置。我已经用找到的解决方案更新了我的问题。 @maasg 很高兴您解决了问题,很高兴看到您发布了您的解决方案 +1【参考方案5】:我不熟悉您提到的 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 还可以通过允许包在运行时将 JUnit 测试贡献给 OSGi 框架来帮助解决此问题,如果您的项目生成可用于测试的可运行 jar,这将非常有用。
【讨论】:
以上是关于在 JUnit 测试的上下文中使用 OSGi 声明式服务的主要内容,如果未能解决你的问题,请参考以下文章
如何在使用 @RunWith 和 @ContextConfiguration 注释的 jUnit 测试中访问 Spring 上下文?
在不创建上下文的情况下禁用 Spring 和 Junit5 测试