Selenium中被误用的XPath

用Selenium实现自动化测试的过程中,如果选择页面上的元素并且对之进行各种操作,是一个常见的任务。Selenium提供了多种定位方法:

  • id:最有效、最方便的方法
  • name:跟id类似的
  • class name:对某些具有相同类的元素一网打尽的好方法
  • link text 和 partial link text: 用在定位超链接上比较多
  • tag name:与class name有点类似
  • css selector:如果你试用jQuery,这个一定是你喜欢的方法
  • xpath:。。。 /html/body/div/div[2]/div[2]/div[2]/div[5]/div/p[2]

网上很多Selenium的介绍文章,在讲述如何利用XPath定位元素的时候,通常都是这样子说的“打开Firefox浏览器,安装Firebug插件,然后就能方便地获得该元素的XPath了”。由于不求甚解,在一段时间内我真以为这些看起来没什么意义,中间穿插着各种数组操作,读起来反人类反社会的所谓XPath就是真的XPath,同志们大家都被误导了。

什么是XPath:http://www.w3.org/TR/xpath/
XPath基础教程:http://www.w3schools.com/xpath/default.asp

XPath在Selenium测试中有好些缺点:1. 性能差,定位元素的性能比起大多数其他方法要差;2. 不够健壮,XPath会随着页面元素布局的改变而改变;3. 兼容性不好,在不同的浏览器下对XPath的实现是不一样的。如此多的弱点,为什么它还存在于Selenium中呢?Selenium提供了这7个元素定位的工具,就好像工具箱里面有锤子有老虎钳有螺丝刀,每个工具都能完成特定的任务,前提是要在正确的前提下,正确地使用。

XPath通常会在如下场景:一个写自动化测试的人,发现他想要操作的元素不能通过id, name, link text等比较方便有效的方法来进行定位,苦逼的他没能说服开发这个页面的人把他想要的id加上,他开始用所谓的XPath来定位元素,代码中充满了各种让人摸不着头脑的XPath(/html/body/div/div[3]/div[2]/div[4]/p[2]),在我看来这样的代码跟录制出来的脚本没有任何区别。可读性差,几乎不能维护。XPath理论上可以这样使用,但是实际上应该避免这样的使用。

XPath的一些优点是大家需要知道的,例如:1. XPath可以通过某个元素找到它的祖先(Ancestors);2. 可以做布尔逻辑判断,例如/button[@value=’submit’ or @name=’tijiao’]

回到上面的场景,假如说那个苦逼的人想定位到页面上的一个提交按钮,这个按钮不能通过id或者name来定位。这个时候他要做的事情不是打开Firebug定位提交按钮右击鼠标再点“Copy XPath”。而是应该是找开发把id或者name加上。如果不行,解决思路可以是:1. 找到该按钮的特征,例如按钮的文字是 submit;2. 用XPath定位,可以这样写://button[@value=’submit’]。

我个人对使用XPath比较反感的,如果可能的话,尽可能使用id或者name。真的要用XPath,千万千万不要打开Firebug定位提交按钮右击鼠标再点“Copy XPath”。先认真学习XPath,后使用。在很长一段时间里面,我对XPath真的是恨之入骨,恨不得先杀之而后快,但是想到存在就是合理,那么多大牛们都没有把XPath摒弃与Selenium之外,XPath必然有它的价值。最近花了点时间学习了一下XPath,并且读了一些关于如何在Selenium里面正确使用XPath的文章,豁然开朗。

参考文章:

云测试服务Sauce Labs介绍

前些天看了一个半标题党文章《Keeping Selenium Tests 100% Blue》,了解到了Sauce Labs这个公司。Sauce Labs是一个提供自动化功能测试的云测试服务公司。Sauce Labs的团队介绍很有意思,CTO是放在第一位,接下来是CEO。他们的创始人兼CTO:Jason Huggins,是Selenium的作者。看了以后我对他们的服务质量还是挺有信心的。

言归正传,Selenium在web自动化测试方面用的想当地广泛,它的一个吸引人的地方就是写一个测试可以测试N个平台的M个浏览器的Z个版本。但对于小团队的话,维护这么多VM系统不现实。我觉得Sauce Labs对于Start up公司来说应该还是挺有吸引力了。在“云”里面有各种VM供你试用,我注册了一个账号试用了一下。

他们提供两种服务,Sauce Scout是手工测试的工具,用户可以选择操作系统和浏览器版本,然后就能拿到一台VM进行手工测试了;Sauce OnDemand是自动化测试服务,用户在本地写好脚本,然后脚本是运行在他们的云里面。

下面主要介绍Sauce OnDemand吧。如果你之前写过Selenium的测试脚本,那迁移到Sauce OnDemand的成本是很低的,只需要把本地的webdriver(接下来都会以WebDriver做例子)改成他们的remote driver就ok了。我是直接用的他们提供的example脚本:https://saucelabs.com/docs/ondemand/getting-started/env/python/se2/mac

跑完脚本以后看结果,这里才是重点。每跑一次任务,在他们的系统里面都会生成一个Job,这个Job所包含的信息想当详细。挑一些我认为比较有用的介绍:

  • Build,如果你是通过CI系统来发起一次自动化测试的话,可以把Build的版本号记到Job里面
  • Tags,这次跑的是Smoke还是Full的测试呢?是一个RC版本还是Live版本
  • Custom Data,以上简单的信息都满足不了你的需求,可以自己传一个JSON格式的对象进去

测试出错怎么办?一切都在云?

  • 视频回放功能 – 这个太好了,可以看看失败的测试当时是怎么跑的
  • Log – 每一个操作都有Log,用什么方法拿到一个元素,输入了些什么东西
  • 截图 – 某些操作会带有截图,也很有用

而且这些Log和Video都是可以下载的。

以上就是Sauce OnDemand的简单介绍,那可能有人会说,这样的功能,我自己的本地跑测试就可以了,为什么还要花钱搞到云里面去?把自动化测试并行起来!

假如执行1个自动化测试用例需要1分钟;覆盖10个平台+浏览器,串行执行就需要10分钟。假如并行执行,只需要1分钟就完成了。听起来很有吸引力吧。这里有一个用Python并行执行测试大概例子(不完整,没有真的调用测试,但是演示了如何进行并行):https://gist.github.com/511658 更多其他语言的并行测试例子: http://saucelabs.com/blog/index.php/tag/parallel-testing/

以上讲了那么多,貌似我说的是在测试“外网”站点啊!我不想先发布后测试,我要测试内网站点,怎么办?他们有个Sauce Connect可以搞定这个问题:) 另外还有一些API让用户获取测试结果。

总结:Sauce Labs提供了针对Selenium实施自动化测试的云服务,和别的云服务有点不一样,他们没有号称使用他们的服务可以降低成本:)

SST (selenium-simple-test) 介绍

今天扫博客发现一个新的自动化测试框架,就是这个SST (selenium-simple-test) ,地址:http://testutils.org/sst/ 项目在这里:https://launchpad.net/selenium-simple-test

SST是在selenium python binding的基础上,抽象出一些称之为actions的API,还有整合了一些工具,例如SST自带一个测试报告模块,测试结果可以以console, html和xml格式展示。并且提供了一个简单的以目录形式管理的测试用例的管理方式。

我试用了一下,他主要还是走selenium的路子,把所有操作都放在sst.actions里面,脚本写出来就是一条条的操作。如果是webdriver的思路的话,就是先有一个浏览器对象,然后针对这个浏览器对象进行操作。

from sst.actions import *

go_to('http://www.ubuntu.com/')
assert_title_contains('Ubuntu')

# Search ubuntu on ubuntu website...
keyword = 'ubuntu'
write_textfield('edit-keys', keyword)
click_element('edit-submit')

# Verify search result page
assert_text('edit-keys', keyword)
assert_element(id='search-results-container')

这个是作者的博客,大家可以去吐吐槽。http://coreygoldberg.blogspot.com/2012/01/officially-introducing-sst-python-web.html

对于这个框架的看法:

  • 多一种选择总是好的,尤其对于使用selenium的python朋友
  • 对比面向对象的调用方式,其实我本人挺喜欢这种selenium风格的脚本
  • 项目才0.1.0,存在各种风险,用到production需谨慎
  • 项目计划未知,这可能就是最后一个版本 :P
  • 大家都喜欢重复造轮子,有重复造轮子倾向的可以以这个轮子为基础继续造下去,哈哈

解决Jenkins Email Extension Plugin发送邮件失败

昨天用来测试的虚拟机不知道怎么回事,Firefox自动升级到8.0。结果就是我的WebDriver PythonBindings 在FF8下面是用不了的。现象就是,实例化一个Firefox Driver,然后 driver.get(‘http://www.google.com’),立刻返回,浏览器没有做任何响应。这个问题我自己不知道怎么解决,外面的世界貌似也没人遇到这个问题,究竟是没人碰到这个问题呢,还是很少用人PythonBinding?

恢复了VM镜像,然后让Jenkins重新跑起来,但是之后就遇到一个问题,发邮件一直失败。错误是

ERROR: Could not send email as a part of the post-build publishers.
javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 553-5.1.2 We weren’t able to find the recipient domain. Please check for any
553-5.1.2 spelling errors, and make sure you didn’t enter any spaces, periods,
553 5.1.2 or other punctuation after the recipient’s email address. i6sm163233obl.2

搜了一下,应该是Jenkins标准的邮件插件是用空格来做多个邮件地址的分隔符,而Email Extension Plugin使用逗号分隔多个邮件地址的。但是这应该是一个很早之前的问题,已经被fix了。

傻傻地build了好多个以后,我想起来,恢复VM以后我是直接把之前的config.xml拷贝回去那个任务的文件夹里面的。进去打开那个config.xml,找到这一行:

<recipientList>$PROJECT_DEFAULT_RECIPIENTS</recipientList>

删掉。就好了。

Hudson保存WebDriver测试执行失败的截图

之前一篇文章介绍了如何把remote driver出错时候的截图保存下来,今天分享一下在Hudson下如何把这些截图保存下来。

用Hudson来跑自动化测试,其实就是把运行自动化测试看作是构建一个软件。在Hudson里面有一个功能叫“Archive the artifacts”,可以把构建的产物(就是所谓的artifacts)打包。具体做法就是

  1. 在Build的最后添加一个步骤,把所有测试过程中生成的.png文件,拷贝到workspace。并且记得删掉原图,要不然下一次测试还是会把之前出错的截图也一起打包
  2. 勾上“Archive the artifacts”
  3. 在“Files to archive”里面填“*.png”
如果你的测试每次都有异常,那么恭喜你(What???),这个build会跑的很顺利。因为每次都有一些.png文件生成,每次打包都能找到一些.png。但是如果有一天,你的自动化测试或者被测系统变得健康起来了,没有错误了,那就会遇到一些麻烦。
作为一个持续构建工具,Hudson会认为,所有build都是理所当然地有一些artifacts。如果没有任何artifacts生成,那做这个build干吗?当测试都顺利跑过,没有生成错误截图的.png文件的时候,这个build也会fail,因为打包的时候没有找到任何文件。这个时候可以这样做。
  • 在Build的最后一步再加一个步骤,在workspace目录中随便生成一个.png文件,例如“touch pass.png”
  • 直接在Ant脚本里面添加一个任务,<touch file=”pass.png”>

Hudson + WebDriver 组织自动化测试

之前介绍过如何使用TestNG来驱动WebDriver用Ant来自动化运行测试。今天分享一下如何把这些东西都放到HudsonJenkins也行)里面呢?

Hudson是一个比较流行都持续集成工具。用Hudson来驱动自动化测试的好处有以下这些:

  • 类似crontab的自动任务管理
  • 丰富的插件支持
  • 支持分布式任务
  • 容易部署

其实整个过程很简单,把Hudson跑起来,新建一个Job,配置一下Ant任务就好了。这里只分享一下我遇到的一些坑。

是否使用Source Code Management获取最新的测试代码?

如果每次测试都拉最新的代码,好处就是保证测试代码是最新的。但是也会带来一些问题,测试代码本身也是代码,怎么保证最新的测试代码没有问题呢?我个人认为,如果团队比较小,可以直接拉最新的代码;如果团队大,需要控制。

如果不用SCM插件,怎么样更新自动化测试代码?

我想到的一种办法就是,在Hudson里面建立一个构建自动化测试代码的Job,这个Job的产物就是自动化测试的包,譬如说如果用WebDriver或者Selenium,就把测试代码build成一个或者若干个jar包,然后建立一个latest的软链接指向最新的jar包;在运行自动化测试的Job里面做好配置,运行测试的目标jar包就指向latest.jar就OK了。

TestNG的结果如何跟Hudson整合

Hudson插件很多,可以用testng-plugin来完成这个任务。配置比较简单,在Ant脚本里面配置好TestNG的result output,然后在Hudson里面把测试报告的模式填好。我直接填的TestNG的默认结果文件“testng-results.xml”。build.xml节点配置的一个例子:

    
        
            
        
        
    

首先在testng节点指定outputdir属性,然后测试运行完成以后把结果文件移动到Hudson的workspace

怎么样把Ant的参数传递给TestNG

很多时候我们会希望通过ant把一些参数传递给testng.xml,从而使得测试更加灵活。例如传递不用的base_url可以测试不同的站点。还有配置不同的浏览器。虽然之前这篇文章已经介绍了如何把Ant的参数传递给TestNG,但是那个方法有个缺点,如果在测试方法A里面调用了测试方法B,测试方法B是不能拿到Ant传进去的参数的。我的办法比较土,就是首先写好一个testng_base.xml的模板文件,把一些可能经常改变的数值替换成参数,然后用Ant的replace任务做字符串替换。

    
        
        
    

TestNG+Ant自动运行测试

之前一篇博客分享了如何使用WebDriver+TestNG实现UI自动化,现在就要让自动化测试自己跑起来,不需要人工干预。需要用到Ant,以及一些定时任务工具,例如Linux的crontab。

如何在Ant的build.xml里面正确配置TestNG呢?

1. 在build.xml里面定义testng任务,在classpath里面指定testng.jar


2. 在build.xml里面新建一个 叫regression的target


    
    
    
    

    

    
        
    
    
        
        
    

    
        
        
        
    

    
        
            
        
    

在target里面新建一个testng标签,里面需要设置的属性有:outputdir – 测试结果输出目录;classpathref – 那些自动化测试代码的目标路径,通常就是编译完成以后的那个目标路径,例如xxx/bin;delegateCommandSystemProperties – 接受传递命令行参数作为系统变量,这个设置为true可以在调用Ant的时候通过 -Dfoo=value 把参数传递给TestNG;里面还有一个xmlfileset节点,这个节点就是指定testng.xml文件的目录以及具体文件。

regression 的 target 有一个depends属性,意思就是跑regression之前需要做compile,而跑compile之前需要clean,应该很容易理解。直接在命令行里面运行:

ant -Durl=http://www.google.com -f build.xml regression

这里出现了 -Durl=http://www.google.com ,回到之前的配置,delegateCommandSystemProperties=”true”。如果这个参数为true,那么通过命令行的 -D 参数可以把一些变量传递给TestNG。譬如说TestNG的测试方法里面是有@Parameters({“url”})标签的话,就能通过ant -Durl=xxx 来传递url的值给到TestNG。例如

@Parameters({"url"})
@Test
public void search(String url){
    WebDriver driver = new FirefoxDriver();
    driver.get(url);
    WebElement query = driver.findElement(By.name("q"));
    query.sendKeys("Cheese");
    query.submit();
}

如果这样调用:ant -Durl=http://www.google.com -f build regression 。那么就会进入google的首页搜索,如果是: ant -Durl=http://magustest.com -f build regression ,那么就会找不到叫“q”的元素,呵呵。

接下来只要把cron job配好就完成了

15 * * * * ant -f /home/maguschen/workspaces/automation/build.xml regression