前文中我们成功使用 ANT + Maven2 + Cruise 搭建了持续集成环境,实现了 compile 、 test 、 tag 、 deploy 、 publish 等自动化环境。今天我们再接再厉,为其加入 Selenium 集成测试。
关于 Selenium 的介绍和使用方法,在此不做赘述,各位可以借助万能的 google 来入门一下,下面我们直切主题。
集成测试前
部署工程
因为在执行集成测试时,需要工程在 web 容器下正常运行,所以,在 maven 的 pre-integration-test 阶段,我们需要将工程部署到 web 服务器下:
<!-- deploy server --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <configuration> <url>http://localhost:8080/manager</url> <server>xxx-test</server> <path>/xxx</path> </configuration> <executions> <!-- 在 pre-integration-test 阶段注册一个redeploy的目标--> <execution> <configuration> <url>http://localhost:8080/manager</url> <path>/xxxtest</path> </configuration> <phase>pre-integration-test</phase> <goals> <goal>redeploy</goal> </goals> </execution> </executions> </plugin>
启动 selenium server
使用 maven 调用 selenium ,是通过 selenium-maven-plugin 这个插件实现的,配置如下:
<!-- selenium --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>selenium-maven-plugin</artifactId> <executions> <!-- start test server --> <execution> <id>start</id> <phase>pre-integration-test</phase> <goals> <goal>start-server</goal> </goals> <configuration> <background>true</background> </configuration> </execution> <execution> <id>stop</id> <phase>post-integration-test</phase> <goals> <goal>stop-server</goal> </goals> </execution> </executions> </plugin>
我们需要在 pre-integration-test 阶段使用 start-server 目标启动 selenium 服务器,执行完集成测试,则需要在 post-integration-test 阶段使用 stop-server 目标停掉之。
Xvfb
对于 linux 下的集成测试, cruise 可能运行在纯字符界面,因此,当 selenium 启动浏览器时,会报无 xserver 的错误,这时,我们就需要使用 xvfb 这个界面模拟程序来搞定这个问题了。正如下面这个链接所述
http://mojo.codehaus.org/selenium-maven-plugin/examples/headless-with-xvfb.html
<!-- 启动xvfb环境 --> <execution> <configuration> <skip>${noxvfb}</skip> </configuration> <id>xvfb</id> <phase>pre-integration-test</phase> <goals> <goal>xvfb</goal> </goals> </execution>
我们会在 pre-integration-test 阶段启动 xvfb ,然后 selenium 就可以在模拟的 xserver 中启动浏览器了。
但是,我们这样配置之后,虽然 linux 下可以正常运行集成测试,但是在 windows 下,我们的测试失败了。
原因就出在这个 xvfb 上。在 windows 下,没有这个程序来运行, pre-integration-test 阶段自然就失败了 …
我们使用 ${noxvfb} 变量,来决定是否运行 xvfb ,变量的值来自外部。那么我们就可以用类似 mvn integration-test –Dnoxvfb=true 的命令来关闭 xvfb 了。
不过本地频繁的输入这个参数是一个很大的浪费(起码键盘会折寿的说 … ),而且还要人工判断是否是 windows 系统。所以,再次请出 ant 大神:
<!-- 集成测试 --> <target name="it" depends="init"> <echo message="os.name is: ${os.name}"/> <!-- 判断是否需要启动xvfb,条件为os.name包含ix关键字 --> <condition property="needxvfb"> <and> <contains string="${os.name}" substring="nux"/> </and> </condition> <antcall target="it_xvfb"/> <antcall target="it_noxvfb"/> </target> <!-- 集成测试中需要启动xvfb --> <target name="it_xvfb" depends="init" if="needxvfb"> <echo message="need xvfb server"/> <property name="m2.target" value="integration-test -Dskiput=true -Dnoxvfb=false -Dnosession=true"/> <antcall target="maven-run"/> </target> <!-- 集成测试中不需要启动xvfb --> <target name="it_noxvfb" depends="init" unless="needxvfb"> <echo message="NOT need xvfb server"/> <property name="m2.target" value="integration-test -Dskiput=true -Dnoxvfb=true -Dnosession=true"/> <antcall target="maven-run"/> </target>
使用 <contains string="${os.name}" substring="nux"/> 标签来判断系统是否为 linux ,以便决定是否启动 xvfb 。
执行集成测试
Selenium 的 maven 扩展支持两种调用方式:使用使用 surefire 的 junit 调用和 selenese 。
surefire 的 junit 调用
Maven2 为我们提供了 integration-test 阶段来进行集成测试,而一般我们会使用 maven-surefire-plugin 这个插件来调用 junit ,进一步调用 Selenium 的测试用例( Selenium 支持多种语言进行),这一做法也可以找到一箩筐的解决方案,比如这里:
http://blog.youkuaiyun.com/dqatsh/archive/2009/02/20/3913306.aspx
和官方示例:
http://mojo.codehaus.org/selenium-maven-plugin/examples/using-with-surefire.html
正如文中所述, maven-surefire-plugin 已经被默认注册到了 test 阶段,因此我们会使用如下的显示定义来在集成测试阶段调用 junit :
<!-- 集成测试阶段使用surefire插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <!-- 使默认的阶段失效 --> <skip>true</skip> <includes> <include>org/**/*.java</include> </includes> </configuration> <executions> <!-- 定义集成测试阶段--> <execution> <id>itest</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <skip>false</skip> <includes> <!--集成测试代码放置在selenium包中--> <include>org/bae/base/selenium/**/*.java</include> </includes> </configuration> </execution> <!-- 定义单元测试阶段--> <execution> <id>utest</id> <phase>test</phase> <goals> <goal>test</goal> </goals> <configuration> <!-- 避免在集成测试阶段运行单元测试--> <skip>${skiput}</skip> <includes> <include>org/bae/base/**/*Test.java</include> </includes> <excludes> <exclude>org/bae/base/selenium/**/*.*</exclude> </excludes> </configuration> </execution> </executions> </plugin>
注意:由于 cruise 已经支持分阶段执行,因此我们不希望在 integration-test 阶段重复进行 unit-test ,因此,在 utest 目标中,使用了 ${skiput} 变量来由命令行传入 unit-test 开关。
Selenese
除了使用 surefire 的 junit 调用,我们还可以使用 selenium-maven-plugin 的 selenese 目标,它可以脱离 junit ,直接使用浏览器端的 selenium 测试代码进行集成测试。
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>selenium-maven-plugin</artifactId> <configuration> <browser>*firefox</browser> <suite>src/test/selenium/suite.html</suite> <startURL>http://wiki.foochal.org</startURL> </configuration> <executions> <execution> <id>test</id> <phase> integration-test </phase> <goals> <goal>selenese</goal> </goals> </execution> </executions> </plugin>
详细参考: http://wiki.foochal.org/index.php/Maven_Selenium