我试图弄清楚如何使用 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 服务注入依赖项,否则会在超时后失败。
我花了很长时间才完全弄清楚这一点。我希望它可以为将来的程序员同行省去一些麻烦。