(序:已经有好几个朋友问到怎么使用eclipse-Automated-Tests来做插件的测试了,既然是这样,那就写一篇使用入门性质的文章来作统一的回复吧。本文不涉及插件开发的理论)
Eclipse提供了一种可扩展的开放式插件机制,其本身的绝大多数功能都是以插件的方式提供的。同时,任何人都可以根据自己的意愿在Eclipse的任何位置扩展实现自己的插件以完成自己需要的功能。本文不会阐述如何开发这些插件(详细资料请查阅Eclipse自带的帮助文档),而是将重点放在如何搭建Eclipse的测试环境,测试我们自己开发的插件上。
Eclipse的插件必须依赖于Eclipse的运行时环境才可以正常工作,我们可以将Eclipse平台比喻作一个容器(Container),插件必须在容器内才能够正常的运行。而我们常使用的一些测试框架,如JUnit,他本身也可以看做是一个容器,所有的测试集/案例(TestSuite/ TestCase)也必须在这个容器下才可以运行。简单的说,工作代码和测试代码都需要工作在自己特定的容器之中。这不能不说是一个矛盾。
如果我们的系统将业务逻辑和平台(容器)相关的实现区分得非常清楚,那么我们可以避免这样的矛盾。可是一般来讲要完全做到这一点并不是那么容易的。JUnit为了解决这个矛盾也提供了一些针对不同容器的框架,如针对基于Struts构架的Webapp系统的StrutsTestCase for JUnit( http://strutstestcase.sourceforge.net/ ),这个框架提供了两套测试方案:容器内测试和容器外测试。所谓的容器内测试,就是将工作代码和测试代码整合在同一个环境内运行。对于Eclipse而言,虽然JUnit并没有提供这样的测试框架,但幸运的是Eclipse项目小组从Eclipse自身的角度提供了一个测试框架:eclipse-Automated-Tests。
eclipse-Automated-Tests最早是和Eclipse-SDK-2.0.2一起发布的,其目的就是将JUnit测试环境和Eclipse运行环境进行整合。实际上这个测试框架也是Eclipse项目组自己也用来测试Eclipse平台的框架。
下面我们将阐述如何使用这个框架测试Eclipse的插件,在此之前需要说明本文使用的环境:Windows2K-SP4、eclipse-SDK-2.1.2-win32、eclipse-Automated-Tests-2.1.2。
首先下载并解开eclipse-Automated-Tests-2.1.2.zip包,其中包含了8个文件,除了两个说明性质的html文件,其他6个文件分别是用于:
1.eclipse-junit-tests-2.1.2RC3.zip :测试集包文件,这是整个框架最核心的部分,其中包含了针对Eclipse各个部分的测试集,并且这些测试集都是以插件的方式工作的,这一点十分重要。每个测试集都包含了plugin.xml、test.xml两个配置文件:plugin.xml定义了该测试集(插件)的运行环境,其中最重要的部分是定义了运行该测试集需要依赖哪些插件(require标签);test.xml定义了测试集如何运行,这是一个很典型的ant配置文件,其中最重要的部分是定义了测试的入口类。其他的文件还有运行时库*.jar和源代码包*src.zip等。需要额外说明的是,在不更改运行测试框架方式的前提下,这个测试集包文件是无需解开的。
2.JUNIT.XSL :用于生成junitreport的样式文件
3.runtests :unix下启动测试框架的文件
4.runtests.bat :Windows下启动测试框架的文件
5.test.xml :测试框架的主要ant配置文件
6.test.properties :ant配置文件对应的属性文件
至此,还需要copy一个Eclipse的SDK包到这个目录下,然后在命令行中运行runtests.bat(本文都以Windows环境为例,如果是unix系统请自己做相应的调整,以后不再额外说明)就可以以默认的方式启动这个测试框架。我们按照运行的工作流来依次说明涉及的各个部分。
首先是runtests.bat文件,该文件的作用是初始化测试环境并启动测试平台。该文件首先定义了一些环境环境变量,通常这里不用修改。然后他要清除上一次测试留下的文件(报告除外):
if EXIST eclipse rmdir /S /Q eclipse
if EXIST workspace rmdir /s /Q workspace
接下来他会重新解压缩Eclipse包和测试集包
unzip -qq -o eclipse-SDK-*.zip
unzip -qq -o -C eclipse-junit-tests*.zip */plugins/org.eclipse.test*
实际上在我们开发自己的测试集时这样做的确很浪费时间,并不是每次运行测试集都需要全新的平台,所以这里在开发测试集的时候可以先注释掉(删除workspace的部分除外)。当然在最后测试集发布时还是建议按照这样的方式。
接下来他会启动测试平台,并指定了ant使用的配置文件:test.xml。默认的test.xml可以分为6个部分:参数配置、安装环境、启动测试定义、测试定义、启动全部测试、生成报告。安装环境部分同样可以在我们开发测试集时先跳过。还是按照工作流来说明这个文件是如何工作的。默认的target是all:
<target name="all">
<antcall target="jdtdebug" />
<antcall target="ant" />
<antcall target="core" />
<antcall target="relEng" />
<antcall target="help" />
<antcall target="jdtui" />
<antcall target="swt" />
<antcall target="teamcvs" />
<antcall target="teamcore" />
<antcall target="ui" />
<antcall target="update" />
<antcall target="jdtuirefactoring" />
<antcall target="genHtml" />
</target>
一目了然,这里依次调用了每个测试集对应的target,最后生成测试报告(genHTML)。我们以ui为例:
<target name="ui" description="Runs the org.eclipse.ui.tests test.xml">
<antcall target="runtests">
<param name="testPlugin" value="${org.eclipse.ui.tests}" />
<param name="report" value="org.eclipse.ui.tests" />
</antcall>
</target>
可以看到实际上每个测试集的target都是以这样的方式在调用runtests这个target,不同的是传入参数的值不同,
<target name="runtests" depends="setup" description="...">
<ant antfile="${eclipse-home}/plugins/${testPlugin}/test.xml"
dir="${eclipse-home}" />
<copy file="${eclipse-home}/${report}.xml"
tofile="${results}/xml/${report}_${platform}.xml" />
</target>
在这里再次启动了一个ant任务,并指定了他使用的配置文件:相应测试集目录下的test.xml。然后是copy测试报告的任务。我们还是以ui为例,此时工作流转到eclipse的plugins目录下的org.eclipse.ui.tests_2.1.0文件夹中的test.xml。默认target是run,该target需要依赖于init、suite、cleanup三个target,首先init删除上次测试生成的报告文件(名称匹配方式可能需要根据自己的情况修改),然后suite启动测试集:
<target name="suite">
......
<property name="sniff-folder"
value="${eclipse-home}/ui_sniff_folder"/>
<delete dir="${sniff-folder}" quiet="true"/>
<ant target="ui-test" antfile="${library-file}" dir="${eclipse-home}">
<property name="data-dir" value="${sniff-folder}"/>
<property name="plugin-name" value="${plugin-name}"/>
<property name="classname"
value="org.eclipse.ui.tests.UiTestSuite"/>
</ant>
......
</target>
可以看到这里包含了三部分的测试,他们有不同的临时文件夹(相当于workspace),都启动了一个ant任务,并指定了同一个配置文件:${eclipse-home}/plugins/org.eclipse.test/下的library.xml。而调用的target和传入的参数都是不同的,参数中最重要的是测试入口类。我们以中间一个为例:调用的target是library.xml中的ui-test,入口类是org.eclipse.ui.tests.UiTestSuite,这个类可以在同目录下的uitestssrc.zip中看到其源代码,这是一个很典型的JUnit的TestSuite类(实际上这其中suite里包含的TestCase依然是ui各个部分的TestSuite,在这些分支的TestSuite中才包含了真正的TestCase类。需要额外说明的是,在这个框架中包含了两套测试方案:手动/自动,一般可以从TestSuite的命名上区分:Automated/Interactive)。在library.xml中的ui-test:
<target name="ui-test">
<antcall target="eclipse-test">
<param name="application"
value="org.eclipse.test.uitestapplication"/>
</antcall>
</target>
这里调用eclipse-test并传入一个参数:org.eclipse.test.uitestapplication。可以看出这是org.eclipse.test包中的一个类,需要说明的是实际上org.eclipse.test包是所有测试集的基础,他包含了启动eclipse平台的一些基本公用类,不过通常情况下不用修改。
<target name="eclipse-test">
<tstamp>
<format property="TIMENOW" pattern="HHmmssSSSS"/>
</tstamp>
<property name="vmargs" value=""/>
<property name="launcher" value="org.eclipse.core.launcher.Main"/>
<property name="formatter"
value="org.apache.tools.ant.taskdefs.optional.junit.
XMLJUnitResultFormatter"/>
<property name="extraVMargs" value="" />
<echo message="Running ${classname}"/>
<java classname="${launcher}" fork="true" dir=".">
<classpath>
<pathelement location="startup.jar"/>
</classpath>
<arg line="-application ${application}"/>
<arg line="-dev bin -data ${data-dir}"/>
<arg line="formatter=${formatter},${classname}.xml"/>
<arg line="-testPluginName ${plugin-name}"/>
<arg line="-className ${classname}"/>
<arg line="-noupdate"/>
<arg line="-os ${os}"/>
<arg line="-ws ${ws}"/>
<arg line="-arch ${arch}"/>
<jvmarg line="${vmargs} ${extraVMargs}"/>
</java>
</target>
在这个target中,接收到以前各步传来的参数,启动Eclipse平台,运行相应测试集。此后调用回归至org.eclipse.ui.tests_2.1.0中的test.xml,运行suite之后的cleanup。run的依赖target运行完毕:
<target name="run" depends="init,suite,cleanup">
<ant target="collect" antfile="${library-file}"
dir="${eclipse-home}">
<property name="includes" value="org*.xml"/>
<property name="output-file"
value="${plugin-name}.xml"/>
</ant>
</target>
再次启动一个ant任务,这里应该很明显可以看出是在生成测试报告了,不再累述。测试完毕后在最外层文件夹中会出现一个results文件,其中包含了xml和html两个文件夹,分别存储两种格式的测试报告。
至此,我们已经分析了默认方式下测试框架的运行流程。可以看出其层次是区分得相当细致,这种松耦合的层次结构与Eclipse本身的风格十分吻合,当然这也给我们修改和扩展这个测试框架带来了很大的便利。
以下是一些在把自己的测试集加入到该测试框架中去时应该注意的环节。
首先,也是最重要的环节,测试集也是以插件的方式运行的,这就要求我们的测试必须包含一个正确的plugin.xml文件。在这个文件中可以没有功能扩展标签(extension),但是必须要有运行时库标签(runtime)和依赖项标签(requires)以指明我们的插件类在什么地方可以找到以及运行这些类需要依赖于哪些插件。例如我们在测试wizard类插件时可能会需要打开一个WizardDialog(org.eclipse.jface.wizard. WizardDialog),这就需要在依赖项中加入包含这个类的的插件Id:org.eclipse.jface。仅仅在我们的TestCase中import org.eclipse.jface.wizard.WizardDialog是不行的。
其次,在我们的测试集工程下写一个build.xml用于创建我们的测试集工程的*.jar和*src.zip等,实际上这并不是什么很麻烦的事但绝对是个很好的习惯。
按照已有测试集工程中test.xml的格式编写我们自己的test.xml,入口类最好使用TestSuite而不是TestCase,不同类别的TestCase应该做成不同的TestSuite放到不同的target中,这样做同样也不会带来很大麻烦但是会给我们今后的维护带来很多好处。
将我们的工程文件夹(最好以com.example.xyz.tests_1.2.3命名,_1.2.3之前的部分应该是我们的测试plugin的Id)加入到之前提到的eclipse-junit-tests-2.1.2RC3.zip中的相应目录下。
在主测试配置文件(最外层与runtests.bat同目录的那个test.xml)中加入我们自己的target,同样是按照已有测试集的格式,同时在test.properties中加入相应的定义。
现在运行runtests.bat试试吧,一切顺利的话就可以看到我们的测试集跑起来了。