OSGI组件自动化测试的一种方案

简介:

1. 背景介绍


OSGi称做Java语言的动态模块系统,它为模块化应用的开发定义了一个基础架构。在产品设计中,OSGI是一种规范或者是一种基础结构,以OSGI的形式设计出产品的工作单元。在企业实际的应用中,也会采用spring DM框架来开发OSGI程序,通过maven管理OSGI组件的结构,并且也有多样的设计模式。
本文在介绍OSGI,maven,spring DM的基础上,引入pax-exam对组件进行代码级自动化case的实施方案,并且就其中的优点与不足展开讨论

 

2. 基础知识

 


2.1 OSGI


2.1.1 什么是OSGI

 


OSGi(JSR 291)亦称做Java语言的动态模块系统,它为模块化应用的开发定义了一个基础架构。简单的说,通过OSGi可以在后台对设备组件进行安装、升级或卸载而无需打断该设备的正常运行.
OSGi规范的核心组件是OSGi框架。这个框架为bundle提供了一个标准环境。整个框架可以划分为一些层次:
L0:运行环境——是Java环境的规范
L1:模块——模块层定义类的装载策略,OSGi模块层为一个模块增加了私有类同时有可控模块间链接
L2:生命周期管理——增加了能够被动态安装、开启、关闭、更新和卸载的bundles
L3:服务注册——服务注册提供了一个面向bundles的考虑到动态性的协作模型。

2.1.2 OSGI的特点

 


1. 模块化
在实际产品设计中,各种模块化设计各有特色,OSGI针对模块化提出一个标准,期望能形成一个市场规范。模块化的具体体现就是bundle的形式,bundle是osgi的部署单元,以jar包的形式封装业务逻辑,对外提供可使用的类,在代码复用、重构方面更有优势。
2. 动态性
bundle的状态有installed,resolved,active三种。这几个状态的含义可理解为:install是bundle能被正确编译,编译完毕之后被OSGI容器解析正常后为resolved,在容器内启动后为active状态,如下图:
 


Bundle的启动、卸载可以在服务运行时进行,这便是它的动态性。


2.1.3 OSGI的classloader


Osgi容器为bundle创建classloader,每一个bundle都有独立的classloader,class loader之间形成一个依赖关系图:
 



在实际运行环境中,Bundle 的 Class Loader 根据如下规则去搜索类资源。规则简要介绍如下:
1. 如类资源属于 java.* 包,则将加载请求委托给父加载器;
2. 如类资源定义在 OSGi 框架中启动委托列表(org.osgi.framework.bootdelegation)中,则将加载请求委托给父加载器;
3. 如类资源属于在 Import-Package 中定义的包,则框架通过 Class Loader 依赖关系图找到导出此包的 Bundle 的 Class Loader,并将加载请求委托给此 Class Loader ;
4. 如类资源属于在 Require-Bundle 中定义的 Bundle,则框架通过 Class Loader 依赖关系图找到此 Bundle 的 Class Loader,将加载请求委托给此 Class Loader ;
5. Bundle 搜索自己的类资源 ( 包括 Bundle-Classpath 里面定义的类路径和属于 Bundle 的 Fragment 的类资源);
6. 若类在 DynamicImport-Package 中定义,则开始尝试在运行环境中寻找符合条件的 Bundle 。

 

2.2 Spring DM


Spring DM即Spring动态模型,允许开发者构建Spring应用程序,这种应用程序能够在osgi容器内进行部署。可以理解为spring与osgi的结合,带来的优点是:
1. 使用spring框架在模块之间实例化、配置、集成组件
2. 开发者使用熟悉的编程模型开发osgi平台程序,使编写osgi程序变得容易一些



如上图解释下spring dm这个框架的一些功能特性:
1. spring extender bundle:负责实例化业务组件的application context,并且这个过程是异步的,也就是说bundle可以先全部被置为active状态,但是实例化application context(即实例化配置文件中定义好的bean,导出可用service)这个过程是异步的,bundle的状态和application context是否实例化之间没有直接关联
2. application context:对于传统的spring框架开发的程序就一个上下文文件,spring容器通过该文件配置的bean进行实例化,对于osgi的程序,每一个bundle可以有自己的application context,声明bundle本身的导入和导出的service
3. 注册service:如下是application context中配置的bean与service信息,bean被实例化之后其中的接口被注册,被注册的service可以为其他bundle所访问。




2.3 Maven

 


Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。使用Maven的时候,用一个明确定义的项目对象模型来描述项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。

Maven的功能与Ant类似,管理项目而用,对于osgi项目,组件众多,依赖关系众多,通过mave的组织管理方式,能方便的处理这些问题


2.4 Pax-exam


说到测试框架,spring DM自带了一个集成junit的测试框架spring-dm test,在实际使用中,因为需要tester也要定义一大串的依赖关系,并且对组件的启动顺序有要求,提高了环境调试的工作量,转而调研了pax-exam这个框架。

Pax-exam是用来测试osgi框架和应用程序的工具。它负责的自动化case执行顺序大致如下:
1. 启动一个OSGi的容器,比如equinox,flex,这是基础,因为所有的OSGI组件都要生存在一个OSGI容器之上
2. 启动需要用到的所有bundle,包括被测bundle,以及被测bundle依赖的bundles
3. 动态的将test代码project创建为一个bundle
4. 把test bundle发布到已经启动了的OSGI容器内
5. 执行test函数

Pax-exam由多个组件组成,如下图:




3 测试方案

 


3.1 测试开发环境

 


3.1.1 JDK
JDK:1.5以上,JDK需要配置环境变量

3.1.2 Eclipse
Eclipse:笔者使用的版本3.5.1,所需安装插件有:svn,maven(详见下方)
3.1.3 Maven
Maven:2.2.1,安装与使用如下说明:

1. Maven的eclipse插件配置:
第一:下载apache-maven-2.2.1-bin.zip

第二:在eclipse的window-preferences-maven-installation中add maven2.2.1如下并选择:
 



第三:maven本地仓库配置,在eclipse的window-preferences-maven-user settings中配置如下

Settings.xml中的两处配置注意为:
一个是本地仓库的路径
<localRepository>E:/maven_eclipse/maven-repository</localRepository>
另一个是线上仓库的地址
<url>http://maven.scm.baidu.com:8081/nexus/content/groups/public</url>

 

2. Maven的环境变量配置
想要在eclipse中执行junit的函数,需要从环境变量得到maven程序的路径,配置如下
 

然后在Path环境变量中,增加%M2_HOME%\bin;如下:



3.1.4 Pax-exam


Pax-exam:2.0.0-RC4

测试代码的pom.xml中需要配置以下对pax-exam的依赖关系

 





3.2 测试思路


3.2.1 代码关系与管理
如2.4中提到的,osgi组件的代码级case,思路都是把测试代码发布为bundle,由osgi框架触发执行测试bundle中的test函数,而test函数的逻辑就是对被测bundle的接口进行调用,如下图所示:

那被测代码、测试代码、被测和测试代码之间究竟是怎么样的组织关系?因为传统的方式是import jar包到build path下,使用maven是在pom.xml中定义对jar包的依赖。为了更好的说明maven组织管理bundle之间的依赖关系,可参考下图示例:
 




说明:
1. 左侧的parent,sub2,sub2/sub1是被测代码结构的示例,这是一种继承结构,parent也是maven模块,类似于面向对象语言中的父类。Parent一般定义了工程需要依赖的第三方的bundle,那么sub2以及sub2的子模块直接从parent继承下来这些第三方的依赖关系。
2. 本地仓库的作用是从线上仓库download bundle下来,开发和执行case时都是到本地仓库去寻找bundle的。那么在定义parent的依赖组件关系后,编译parent时maven就会执行download的过程
3. 被测代码通过mvn install的命令在开发环境下编译,编译后的jar包会存放至本地仓库;如果被测代码已经成熟,则可以直接发布到线上仓库
4. 测试代码的结构可以简单的就是一个maven project或者仿照被测代码的集成结构
5. 测试代码所依赖的第三方组件,被测代码的组件,都在本地仓库


3.2.2 Pax-exam junit用法


Pax-exam的几个主要的功能是:将所有的bundle解析、加载、启动,执行单测case。下面以一个单测用例的例子做说明。



第一句@RunWith通过注解使得该class被Junit4TestRunner来驱动执行,Junit4TestRunner在2.4节中提到过,是封装了Juit4的TestRunner。做的事情就是在组件容器内找到TestClass和TestMethod,并且通过反射调用执行单测函数。

第二句的注解是pax-exam2才新升级的功能,2.4提到过pax-exam执行test case的流程,最开始就是要启动测试容器,然后启动所有的bundle,这个注解有两个class可选,分别为:
AllConfinedStagedReactorFactory.class
这个类使得test case执行时,每一个@Test函数都要启动一次容器,并且启动所有的bundle,开销较大。一般不建议这么做。
EagerSingleStagedReactorFactory.class
这个类是让每一个test class初始化时启动容器,并且启动所有bundle,class中的所有@Test函数就在这已经启动好的容器内执行了。这样减少了启动容器的次数,减少开销。

第三句@Configuration()这个注解是pax-exam本身所需设置一些配置项,包括了配置需要启动哪些bundle,所以这个是重点。以上述代码片段为例说明,都需要配置哪些内容:
mavenBundle().groupId("javax.servlet").artifactId("com.springsource.javax.servlet").version("2.5.0"),
这一句比较有代表性,用来配置pax-exam测试执行时所需启动的bundle信息。被测代码需要启动什么bundle,pax-exam测试时就需要在这里进行配置,否则容器启动就会失败。

@Test注解就跟Junit4一样了,标明函数有Junit4来执行。其代码逻辑中,如何调用被测的接口呢?
还得接2.2节来说,采用spring-dm开发,bundle之间互相通过接口来通信,那么接口就是通过spring来实例化的。BundleContext这个对象,能够获得组件容器内可访问的接口,因此调用被测接口时一般为:
XService x = bundleContext.getServiceReference(XService.class)
这里XService就是某个bundle在spring的applicationcontext文件中配置的bean,实例化之后,通过bundleContext就可以得到。

 

3.2.3 测试执行


有两种方法来执行pax-exam的junit case。
第一是在开发环境下,使用eclipse的junit4和maven插件来执行。在test class上右键run as Junit test,当然要这么执行需要关注3.1.3中maven的环境变量配置,因为这么执行需要告诉junit,那些依赖的bundle去哪里能拿到。
第二是在命令行环境下,使用maven的命令:mvn test执行。在2.3节中提到过,maven定义了一些插件的目标,所谓的插件就是具有特定功能的程序,能在maven中配置使用,比如要执行mvn test,需要有一个maven插件maven-surefire-plugin,在测试代码的pom.xml中配置为:

 

 



如上,在pom中以plugins标签来配置maven-surefire-plugin,在configuration标签中有几项需要说明下:
Parallel和threadCount配合使用,可用以多线程执行单测case,不过对于osgi组件级的测试,多线程的方式会明显加大开销,不建议这么做。如果是非osgi的程序,可以通过多线程方式加快单测执行时间。
forkMode可以是once,pertest,与agrline配合起来可以为case执行时启动jvm传入参数,forkMode的含义是jvm初始化的机制,分别为:只初始化一次,每一个case初始化一次。
Mvn test执行完毕的report就是junit的结果report,为xml格式。

 

3.2.4 代码调试


在单测实施中,如果抛出的异常还不够看出原因所在,可通过代码调试来跟踪执行情况,从而定位问题。
Osgi组件,因为每一个组件都有自己的classloader,测试代码是一个组件,都是组件的话并不能直接调试,只能采用远程调试的方式。如下代码意思是在整个bundle容器的jvm启动后,开放一个监听端口以便远程调试:

new VMOption( "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" ),
new TimeoutOption(0),

在eclipse的debug configuration中新增一个remote java application,配置上面的地址和端口。设置断点之后执行单测case,就如同本地调试一样,可单步跟踪。

 

3.2.5 覆盖率


考虑到被测程序架构是maven+osgi,覆盖率可选的工具有多种:maven-emma-plugin,jacoco。
Jacoco采用的是动态插桩方式,不会额外的输出一份插桩后的class,而是在jvm中载入class的时候做的插桩,它的使用过程如下:

4 持续集成方案

 

进行持续集成之前,需要考虑到,程序架构为maven+osgi+pax-exam junit case+jacoco,各个环节都有自己的特别之处,先以一个流程图说明下,在如此架构下ci的执行过程:

5 小结


Osgi组件的架构,初衷是引入模块化的设计理念,就好比搭积木一样,一块一块的垒起来。不过也带来一些问题就是框架重量级,初期开发难度大。随之而来的自动化测试或者单元测试也面临同样的问题。
Pax-exam是一种osgi组件自动化测试的框架,上文都是基于这个框架设计的方案,它有哪些优势和不足呢?
优势:比较于spring-dm自带的test框架,pax-exam不需要依赖一个庞大的pom.xml文件,而是自己配置,并且能自动理清各种bundle的前后依赖和启动顺序,这一点很重要,否则N多个bundle如何启动都是个问题。
不足:开销较大,主要体现为执行测试时,需要把要依赖的bundle都配置并且启动,这个过程是比较花时间的,比如启动容器和组件需要7s,而执行case本身都是毫秒级。

后续的计划:pax-exam在实际的工作中带来一些便利,但是为了减少开销,也为了提高编写单测case的易用性,后续的方向会向spring容器偏移,spring容器与组件容器相比,确实是轻量级的。

 

作者:xdwei










本文转自百度技术51CTO博客,原文链接:http://blog.51cto.com/baidutech/743812,如需转载请自行联系原作者

相关文章
|
1月前
|
机器学习/深度学习 人工智能 监控
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
|
19天前
|
JavaScript 测试技术 API
Jest进阶:测试 Vue 组件
Jest进阶:测试 Vue 组件
|
19天前
|
Web App开发 定位技术 iOS开发
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
20 1
|
20天前
|
前端开发 JavaScript 安全
学习如何为 React 组件编写测试:
学习如何为 React 组件编写测试:
35 2
|
1月前
|
机器学习/深度学习 存储 测试技术
从0到1:如何规划一套流量回放自动化测试方案
本文介绍了流量回放自动化测试的完整方法,从企业战略到交付的四个关键环节:Discovery(深度挖掘)、Define(定义目标)、Design(详细设计)和Delivery(交付与反馈)。通过这些步骤,帮助企业优化系统性能和稳定性,确保产品的高质量。
55 4
|
1月前
|
存储 NoSQL 大数据
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
大数据-51 Redis 高可用方案CAP-AP 主从复制 一主一从 全量和增量同步 哨兵模式 docker-compose测试
34 3
|
3月前
|
测试技术 Linux 虚拟化
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
详细的VMware虚拟机安装macOS Big Sur的保姆级教程,包括下载VMware和macOS镜像、图解安装步骤和遇到问题时的解决方案,旨在帮助读者顺利搭建macOS虚拟机环境。
142 3
iOS自动化测试方案(五):保姆级VMware虚拟机安装MacOS
|
3月前
|
测试技术 数据安全/隐私保护 iOS开发
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
iOS自动化测试方案的第四部分,涵盖了基础环境准备、iPhone虚拟机设置、MacOS虚拟机与iPhone真机的连接,以及扩展问题和代码示例,确保读者能够顺利完成环境搭建并进行iOS自动化测试。
266 0
iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境
|
1月前
|
机器学习/深度学习 人工智能 运维
构建高效运维体系:从自动化到智能化的演进
本文探讨了如何通过自动化和智能化手段,提升IT运维效率与质量。首先介绍了自动化在简化操作、减少错误中的作用;然后阐述了智能化技术如AI在预测故障、优化资源中的应用;最后讨论了如何构建一个既自动化又智能的运维体系,以实现高效、稳定和安全的IT环境。
65 4
|
1月前
|
运维 Linux Apache
,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具
【10月更文挑战第7天】随着云计算和容器化技术的发展,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具,通过定义资源状态和关系,确保系统始终处于期望配置状态。本文介绍Puppet的基本概念、安装配置及使用示例,帮助读者快速掌握Puppet,实现高效自动化运维。
54 4