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的文章,豁然开朗。

参考文章:

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
  • 大家都喜欢重复造轮子,有重复造轮子倾向的可以以这个轮子为基础继续造下去,哈哈

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

WebDriver + TestNG 应用

Selenium 2 已经发布了一个多月,官方版本已经到了Selenium 2.3,并且在Google code里面可以找到2.4的下载。Selenium 2 最大的更新就是集成了WebDriver。这两者是什么关系呢?如果你搜索WebDriver,第一条结果是Selenium。其实WebDriver和Selenium可以说是在实现UI Automation的竞争对手。Selenium是运行在JavaScript的sandbox里面,所以很容易就支持不同的浏览器;而WebDriver则是直接操作浏览器本身,更接近用户的真实操作,但正因为如此,所以WebDriver在多浏览器/操作系统的支持上就要落后于Selenium。不过从Selenium 2开始,这两个项目合并了,可以继续用原来的Selenium,也可以考虑迁移到WebDriver。我个人认为WebDriver应该是以后的大趋势,还是值得迁移的。至于你信不信,我反正是信了。

作为一个轻量级的UI Automation框架,需要写一些驱动它的代码,大部分人会选择JUnit,因为JUnit是单元测试的事实标准;但是我会用TestNG。这些UI Automation的东西,它们本身不是单元测试,而且也没有太多单元测试的风格。

从一段简单的测试开始

public class GoogleTest  {
    @Test
    public void search(ITestContext context) {
        WebDriver driver = new FirefoxDriver();

        driver.get("http://www.google.com");

        WebElement element = driver.findElement(By.name("q"));

        element.sendKeys("magus");
        element.submit();

        Assert.assertTrue(driver.getTitle().contains("magus"), "Something wrong with title");
    }
}

TestNG应用了Java的Annotations,只需要在测试方法上面打上@Test就可以标示出search是一个测试方法。用TestNG运行测试还需要一个testng.xml的文件,文件名其实可以随便起,没有关系的。


    
        
            
                
                    
                
            
        
    


我想让测试更加灵活,1. 可以配置使用任意支持的浏览器进行测试;2. 配置所有Google的URL;3. 配置搜索的关键字。修改后的代码:

public class GoogleTest  {
    WebDriver driver;

    @Parameters({"browser"})
    @BeforeTest
    public void setupBrowser(String browser){
        if (browser.equals("firefox")){
            driver = new FirefoxDriver();
        } else {
            driver = new ChromeDriver();
        }
    }

    @Parameters({ "url", "keyword" })
    @Test
    public void search(String url, String keyword, ITestContext context) {        driver.get(url);
        WebElement element = driver.findElement(By.name("q"));
        element.sendKeys(keyword);
        element.submit();
        Assert.assertTrue(driver.getTitle().contains(keyword), "Something wrong with title");        }
}

testng.xml


    
    
    
    
        
            
                
                    
                    
                
            
        
    

利用TestNG的@Parameters标签,让测试方法从testng.xml里面读取参数,实现参数化。在testng.xml的配置中,test节点需要增加一个属性的配置: preserve-order=”true”。这个preserve-order默认是false,在节点下面的所有方法的执行顺序是无序的。把它设为true以后就能保证在节点下的方法是按照顺序执行的。TestNG的这个功能可以方便我们在testng.xml里面拼装测试。假设我们有很多独立的测试方法,例如

  • navigateCategory
  • addComment
  • addFriend
  • login
  • logout

就可以在testng.xml里面拼出不同的测试,例如


    
        
            
                
                
                
            
        
    


    
        
                            
                
                
                
            
        
    

TestNG比JUnit更加适合做一些非单元测试的事情,不是说JUnit不好,而是不能把JUnit当成万能的锤子,到处钉钉子。WebDriver的API比Selenium的更加简洁,会是以后的大趋势。

之后打算分享一下如何用ant把自动化测试自动化起来。

敏捷开发中开展自动化测试的经验

上周在51testing上看到一个问题,题目是在敏捷开发中,自动化测试应该如何开展呢?当时在论坛上回复了一下,现在放到博客,稍微调整一下。

首先,敏捷开发并不是部分同学想象中的那样,没有文档没有需求,开发来了就干,干几个月就丢给客户一个版本让他们用去。我们公司一般6个星期是一个release周期,在这6个星期里面,可以做的事情是非常多的。

  • 需求,需求通常来自于PM,在一个release周期的开始,QA通常没太多事情需要做,比较轻松,这个时候一个比较重要的工作就是跟PM沟通当前release里面的一些feature的情况。在这个时候,QA可以做一些自动化测试的准备。例如在某个release周期里,我知道在接下来的测试当中我需要频繁地比较CSV文件,那么作为QA就应该在项目还不是很紧张的时候,就开始准备自动化测试的脚本,例如刚才说的这个CSV文件比较工作。
  • 开始开发,如果公司是实时TDD开发,那么这个时候QA可以做的事情大概有2个,帮助开发写单元测试用例,并且实施自动化测试(主要是单元测试),另一个是review(虽然不是自动化测试的内容)。如果不是采用TDD开发,那么QA做的事情跟上一个阶段的做的差不多。
  • 正式提交测试,OK,这个时候是我们QA比较忙的时候,有可能出现几个情况,1. 跟我的预想一样,我真的需要一个CSV文件比较工作,并且只需要这一个工具,并且我已经完成了,那么就可以进行测试了。2. 可能有一些新的自动化测试需求跑出来了,例如每天晚上自动比较几万个CSV文件并且把测试结果发给相关的人,这时候作为QA,在考虑资源允许的情况下,应该尽早完成这个工具,而不是每天晚上爬起来看结果。
  • 发布完毕以后,回过头来看工具,是否有值得改进的地方,是否能够改进一下就能够给整个Team使用。

以上算是一个release周期里面的一些微观的工作,宏观上来说需要做点什么事情呢?

现在提到的敏捷开发,都有一个很突出的特点,就是产品快速交付给客户,为了快速交付这个目的,公司里面每个团队都作出了努力,那么具体到QA团队,肯定就是要在保持测试质量得到保证的前提下,尽可能地缩减测试所需要的时间,使得产品按时按质交付。要达到这个目的,总靠一些AD-HOC的工作(例如刚才提到的突然写个CSV比较工具)是不可能达到要求的,那应该如何进行呢?

敏捷开发也是开发,产品不是孙悟空,不会某一天就从石头里面爆出来了。在产品开发的前期(例如0.1, 0.2版本之类),尽可能地想办法搭建一个自动化回归测试的框架,这个框架的特点有:1. 快速完成回归测试; 2.能够快速地添加测试用例并且跑起来;3.能够随着产品的演化而不断改进(不能是那种用1~2个release就要扔的东西);4.维护的成本要低(在一个release周期里面如果自动化测试需求有变化,不应该需要超过1个星期的时间才能改好,当然翻天覆地的变化除外)

综上所述, 我认为在敏捷开发里面的自动化测试是有2条路线,并且这2条路是并行的,缺一不可

  • 至少一个自动化回归测试框架,保证release前能够对产品进行覆盖较为全面的回归测试
  • 工作中*不断地*开发自动化测试工具,提高自己的生产率

以上两点的目的很简单,就是要在保持产品质量处于一个较高水平的情况下,帮助公司尽可能地快速交付新版本的产品。

内部自动化测试交流有感

上周公司组织了一个交流会,主题是关于自动化测试,这个已经在公司引起高层们足够重视的话题,说是交流会,其实我更觉得是个成果展示会,本人代表CORE QA跟大家分享了一下我们组内自动化测试的一些情况,并且在做的过程中的一些经验。我是第一个,下面是VI的自动化测试,VI主要是跟Video播放器结合的比较紧密,最后是UI同事的介绍。我从头到尾都参与,所以说说的我感受吧。

CORE这边测试的特点就是,针对MRM系统的后台进行测试,肩带来说就是模拟各种跟后台打交道的“程序”的工作,进行测试。我们测试有以下特点:

  1. 直接跟后台程序交互,基本没有现成的开源或者商业工具可以支持自动化测试快速开展
  2. 测试验证结果大多数是后台的输入,也就是前台或者是第三方系统的输入,所以验证的方法不能简单地观察输出结果,同时需要知道后台的输出拿到别的系统能否正常工作
  3. 牵涉到数据迁移或者数据重处理的时候,QA需要直接读取生产环境的数据进行校验

由于以上特点,所以我们的自动化测试85%都是自己开发工具来做,常用的脚本语言是Python,经常用到的一些模块包括读取MySQL的MySQLdb;csv模块;re模块;总得原则就是把重复性强,容易引入错误的工作都写成小工具。并且尽可能使用已有的成熟的库,而不是自己重复发明轮子。例如我们的前端页面使用了web.py轻量级框架,JSON库。到目前为止,我自己感觉我们的自动化测试还是做的不错的,主要是以下几点

  1. 简单。说起自动化测试,可能有部分人,或者说是外行的人吧,都觉得这个东西非常酷,人只要倒杯咖啡看着电脑执行测试就好了。但是其实实用有效的自动化测试并不是说看起来有多酷,而是这个东西能把人从重复劳动中解放出来。
  2. 强大。我刚到公司的时候,已经有600多个回归测试跑在自动化框架上,我当时就觉得已经挺不错的,因为这个自动化测试是由大概4~5个不同的人做的,我以前在MySpace的时候SOA大概有300个CASE,不过那都是我一个人做的,相比较而言FreeWheel应该是更好。
  3. 持续改进。虽然我刚到公司的时候自动化测试已经存在并且也算是行之有效,但是任何系统都是有可改进的空间的,我把前端UI改了一下,很高兴可以帮助大家缩短了找问题的时间
  4. 全面。基本上所有的模块都有自己的一堆自动化测试工具。

引用一句我非常喜欢的英语:So far so good。那下面我想做什么事情呢?

  1. 自动化测试其实不是测试,只是重复运行测试用例而已。真正的测试用的是脑,而不是工具,工具只是辅助我们的工作的
  2. 自动化测试是危险的,不要看到所有回归测试都通过了,就高枕无忧
  3. 手动测试才是根本
  4. 希望能给大家灌输一些思想,如果发现自己在重复做一件事情,那么应该停下来,想想有什么办法能够让自己停止重复,尽可能自己解决问题,培养自己的动手能力
  5. 看看有没有一些开源工作能让现在的工作做的更加好

下面说说对VI TEAM自动化测试介绍的一点感觉吧,VI和CORE有点儿相似,就是都是用的自己开发的自动化工具,而没用应用了太多开源工具,我个人觉得这里面原因有2个

  1. VI的测试面向Video播放器的SDK,也是一个后台,所以也没有太多现成的工具
  2. 用户怎么用我们的SDK?就是调用接口,跟CORE面对的问题相似

估计由于经常跟XML打交道,所以VI的自动化测试用到很多XML文件作为配置。由于隔行如隔山,所以没有看懂里面的一些玄机,总的来说就是跟我们CORE有点相似。

我们CORE和VI一样,这些工具如果跳出了这个公司,基本上就不能应用到其他地方,这也是对整个系统来说的底层部分做自动化测试的特点:高度定制化,通用性低,自己开发居多

最后就是UI的介绍,终于等到一个看得懂的啦。

UI那边就是大量使用开源工具,这个也是很有道理的

  1. UI的自动化测试实施难度比后台程序的自动化要大
  2. 现有的UI自动化测试非常丰富

那我们的UI是怎么做的呢?首先UI的同事用了一个持续集成的工具hudson作为一个颗粒度比较粗的测试用例管理工具,hudson作为自动化测试的主心骨,QA们可以在hudson上触发自动化测试的运行,运行完了以后可以看到测试结果,并且,利用了hudson的分布式结构,由多个测试机来执行测试,达到了很好的资源调配。对浏览器的控制方面,用了Selenium,会上没有问UI是否利用了Selenium的多浏览器支持,从演示上来看应该只做的Firefox的。他们的分工很明确,分了专门做功能测试的QA和专门做自动化测试工具开发的SDET,SDET主要是负责写RUBY代码,封装并且暴露了一些通用的方法给QA使用,并且同时使用了Cucumber作为一个DSL,QA是用Cucumber来做自动化测试的一些描述,Cucumber的作用就是对功能测试的QA屏蔽了底层RUBY脚本,对上就是“翻译”功能测试QA的意图,“翻译”成RUBY。说一下我觉得的优点:

  1. 分开了自动化测试工具开发和自动化测试实施
  2. 使用了大量开源工具,提高效率
  3. 而且都是业界常用工具,对以后跳槽帮助不小(嘿嘿)
  4. One click automation (只需要点一下hudson)

一些工具带来的制约

  1. 一次只能运行一批测试,不能重跑单个测试
  2. 个人觉得使用XPATH作为对象的识别并不是一个好的选择

总得来说大家都各有特色,并且都做得挺好,并且都有不少可以提高的空间。多点交流的确能带来不少灵感。

DSL与自动化测试 – 用Python实现简单的DSL

自动化测试,一个在测试领域中被广为熟知,也是被谈论最多的概念之一。DSL (Domain Specific Language),一种高度抽象,用于某个特定领域下编程语言。软件测试在大多数情况下都是对某个特定行业的软件系统进行测试,所以这两者应该可以很好的结合起来,事实上也是这样的,QTP里面的keyword view,其实就是DSL的一个实现。DSL一般可以分为两个大的类型,分别是External DSL 和 Internal DSL (引用自Martin Fowler)。External DSL 一般来说是跟其实现语言不一样的 DSL,常见的External DSL 有:SQL和XML配置文件;而Internal DSL 一般来说就是该DSL使用某个现成的编程语言(就是所谓的host language),然后对host language进行一些改造而成。

我们在测试中会遇到很多问题,其中一些问题,几乎是所有公司所有团队都会遇到的,例如测试覆盖率不够,测试的时间不够等等。面对这些问题,自动化测试自然而然地成为解决这些问题的首选方法。但是自动化测试真的就是银弹麽?不见得!以前曾经在ASP.NET QA 的博客中给他们留言,请教过关于自动化测试的事情,我记得其中有一个回复是说,在某个release中过度地使用自动化测试,一切东西都想实现自动化测试,而忽略了产品本身的功能、特性的关注,结果就是超高的自动化测试覆盖率,但是很差的产品质量。大家都去实现自动化测试了,谁来做功能点的覆盖呢?某些领域的专家(SME),他们可能对测试技术是一无所知的,要把这些领域专家和测试实施结合起来,DSL就是一个比较好的桥梁。

我在工作中遇到的问题是,我需要测试一个类似UV(独立用户访问数)统计的系统,统计UV的方法其实就是根据_uid cookie的值来判断这个用户在某段时间内访问过我们的系统多少次,访问了哪些站点,进行了什么样的行为。其中有2个地方比较麻烦,第一就是在测试过程中要不断地拷贝cookie,这样拷来拷去两三次以后很容易就混乱,出错;第二就是需要记录访问哪些站点,这些站点都只是ID,也是需要不断地修改请求,测试时间长了也是很容易出错。所以我就打算在原来的测试工具基础上,实现一个简单的Internal DSL。先看成品:

@tc
def uniq_inventory_case01():
    test= testTool()
    test.user('a').view('asset55100002').anetwork('55100').onsite('site55100503').snetwork('55100').dnetwork('55100').times(1).go()
    test.user('b').view('asset55100002').anetwork('55100').onsite('site55100503').snetwork('55100').dnetwork('55100').times(2).go()
    test.user('b').view('asset55100002').anetwork('55100').onsite('site55100504_noad').snetwork('55100').dnetwork('55100').times(4).go()

实例化一个testTool对象,然后就是指定哪个用户:user(‘a’)或者user(‘b’),看的视频的ID:view(‘asset55100002’),这个视频属于哪个CRO呢?anetwork(‘55100’);放在哪个网站呢?onsite(‘site55100503’);网站是谁的呢?snetwork(‘55100’);谁是分发者呢?dnetwork(‘55100’);看了多少次呢?times(4);最后一个有点儿丑陋的go()。

像这样子一句话里面N个方法连着用,就叫Method Chaining,Method Chaining通常可以让代码变得更加人性化,读起来更加容易。但是使用Method Chaining通常会遇到一个问题,就是很难判断就是到了哪个方法才是终结呢?是不是有些方法的调用是可选的,有些是必选的呢?其中一个解决方法就是我用到的,放一个.go()方法在最后,作为终结方法。要实现Method Chaining,其实只需要顶一个类,对于需要做连接的方法,最后都返回这个类的实例。例如:

def view(self, assetid):
    if assetid:     self.asset_id = assetid
    return self

def anetwork(self, networkid):
    if networkid:   self.a_network_id = networkid
    return self

def snetwork(self, networkid):
    if networkid:   self.s_network_id = networkid
    return self

def dnetwork(self, networkid):
    if networkid:   self.d_network_id = networkid
    return self

def onsite(self, sectionid):
    if sectionid:   self.site_section_id = sectionid
    return self

def times(self, times):
    if times>0:       self.request_times = times
    return self

最后一个终结方法go(),就做真正的处理

def go(self):
    if self.asset_id and self.site_section_id and self.times and self.a_network_id and self.s_network_id:
        self.prepareRequest()
        for i in range(self.request_times):
            self.sendRequest()

        self.cleanup()
    else:
        info = 'Required information missing, abort running.'
        logging.debug(info)
        print info

如果是实现一个External DSL 的话,的确难度不小;但是Internal DSL其实并不是很高深,也不是很难实现,在它的帮助下,可以把工作完成的更好,对自己以后维护测试用例也带来了不少方便。

自动化测试中的sleep

最近在修改公司现有的一个自动化测试框架,里面用了很多time.sleep()方法,看着不是很爽,为什么我觉得sleep方法在自动化测试中不应该过多的使用呢,我甚至觉得应该尽可能避免sleep方法的使用,sleep可以作为增加自动化测试稳定性的手段,但是不能依赖sleep来让自动化系统稳定。

举个例子,如果一个UI的自动化测试,需要等待某个页面load完成以后才进行操作,那么需要对那个页面是否已经Load完成进行判断,而不应该sleep(x),x是一个magic number,有时候1、2秒就足以,有时候它却不知道有多大,因为已经超时了!那如果我们在check页面状态之前做一个短时间的sleep,那么在某些场合下可以增加这个自动化测试的稳定性,但是最终整个自动化测试的脚本是不会依赖于这个sleep的语句来达到稳定的。

在做自动化测试的时候,最常见的两种判断就是1. 某程序已经成功启动,某页面已经加载完毕。 2. 某程序已经正常关闭,某服务已经顺利停止。

回到实际的工作,我要判断被测的程序是否已经正常启动,可以用系统提供的一些工具,或者调用一些接口,例如SNMP命令,或者是调用一下Lua脚本等,如果他们都返回我们期望的数据,那么可以认为程序已经成功启动了。反之,如果前面的这些命令出错了,那么我也可以认为程序已经是关闭了的。

要实现自动化测试,就必须要让测试代码每时每刻都掌握着被测系统的状态,sleep方法会让自动化测试脚本的行为变得诡异