在代码签入之前运行测试

很久没有写文章,看着每天仅有的可怜的访问量,实在觉得对不起读者,决定再写点什么吧。

测试工程师应该做什么?如何体现测试工程师的价值?类似的问题如果放到一些测试会议或者沙龙上讨论,估计一定很热闹。每个人心中都有自己的认识和答案。我个人理解的较为简单,假如整个团队没有测试,会达得到什么样的一种成就?那假如把测试加进去这个团队,能否帮助大伙提高成就?如果测试是在做正功,那么就是有价值,至于价值多少不容易衡量,如果测试在做负功,那还是自求多福吧。

软件天生就有缺陷(Defect, Bug),缺陷从哪来?缺陷从不平白无故地出现,它总是伴随着功能(Feature)而来,谁创造了功能?随便啦,但是最后通常来是开发工程师动手的,所以大部分时间开发都会负责修bug。一般的流程都是:1. 写了一些功能; 2. 测试;3. 发现一些bug;4. 修bug。如果我们可以加速这个反馈的时间,那么也算是做了正功了。

在网上找到一张很好的图: http://www.jetbrains.com/teamcity/features/delayed_commit.html 很好的解释了怎么样在真正提交代码之前运行测试。至于如何做,基本上每个代码管理工具都有类似的支持,例如Git的Hook: http://git-scm.com/book/en/Customizing-Git-Git-Hooks, 又例如一个号称更加好的Hook:https://github.com/jish/pre-commit,SVN也行:http://svnbook.red-bean.com/en/1.7/svn.ref.reposhooks.pre-commit.html

我只分享一下我自己在使用上的一些经验:

  • 如果你的测试是Flaky的,不要放在pre-commit里面去。一切看起来很没好,但是如果当开发经常在写完一段代码正准备提交的时候,被一个fail的测试挡住了,然后花了半天时间发现不是他的代码有问题而是测试有问题,结果谁都不太爽
  • 不要把大量的end to end测试放在pre-commit里面跑,想一下让你跑10分钟测试才能提交代码,你能不抓狂吗?
  • 要让开发写测试,而不是测试工程师写测试。这样做的好处是开发自己了解测试代码,可以自己修改测试,更新测试,而不需要等测试来做修复。另一方面,产品质量真的不是测试工程师能保证的,大家测才是真的测
  • 不要在刚开始的时候就添加过多的测试,少量的测试总是比较容易跑过并且让大家接受
  • pre-commit测试不是万能,千万不要生搬硬套

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

美股投资小记

今年国内CPI狂升,好几个月CPI超过5%,虽然近期有所回落,但是也是4%+的水平。钱放在那里缩水,那还不如找点生钱的门路。炒房?没那个本钱,那只能炒股了。最近比较火的新闻大概就是:中国股市:十年零涨幅。加上国内股市基本上都是内幕交易,不认识人,没法玩。只能看看美股。

第一步:选择券商

我比较懒,只比较了2家券商,都是有中文服务的。一家是Firstrade,另一家是sogotrade。其实大家主要关心的还是价格。Firstrade最近是开户就有90天免费交易,之后每笔交易收费是6.95刀。SogoTrade是新开户前100次交易免费,之后每笔3刀。我开户的时候Firstrade还没有免费90天的活动,那时候好像是免费300次,但是对比了7刀和3刀的交易费,我最后还是选择了SogoTrade。别小看这交易费,每笔3刀,你买进收3刀,差不多20RBM,卖出也是要收这个钱。至于最低金额的限制,Firstrade是没有限制的,SogoTrade最低初始存进去要500刀以上。

第二步:开户

由于我选择了SogoTrade,下面就讲一下在SogoTrade开户的过程吧。首先是注册:可以通过这个地址注册 – https://www.sogotrade.com/s1/NewAccountSetup/Step1.aspx?rf=598726(这是我的推荐地址,意思就是通过这个地址注册,我能得到25次免费交易的奖励)在这边表的下方是可以选择用中文提示的,但是记住,所有需要填的地方都要用英文。名字用拼音,如果你有护照,就按照护照上面的拼音来就可以了。

刚才那个链接只是注册的第一部分,后面还有继续填一些东西,都没有什么难的地方。就是账号类型选择Margin就可以了。当所有东西都填好以后,你会收到一封邮件,标题是:SogoTrade帐户申请接收‏。这时候你要在邮件的N个链接里面选择一个合适的PDF文件,打印,填表,寄过去。但是一般人可能被下面那10几个链接搞晕。没关系,你只要发邮件(可以直接写中文的)给newaccounts@sogotrade.com这个邮箱,他们的客服代表就会根据你的账号类型,给你发一个合适你的PDF文件。他们要很多表,其中大部分表都是可以扫描发送过去给他们的,只有W-8BEN是需要原件寄过去美国的,其他所有表都是打印出来,填好,然后给他们发邮件就好了。中间插一句,一般人都没有扫描仪,其实现在很多手机应用都有这个功能的,推荐一个叫CamScanner的软件,iPhoneAndroid上都有免费版。

关于邮寄W-8BEN表,我当时用的是平邮,花了9块钱。网上很多人都是用EMS或者挂号信,花了100多,这个就见仁见智了。

第三步:汇款

等SogoTrade收到你所有的材料以后,就会给你分配一个账号,之前是纯数字的,最近经过升级,变成了数字和英文混合的。去银行,中行或者光大,填电汇单子,其实所有信息在SogoTrade网站上都会提供的,最关键的就是把自己的SogoTrade上注册的真名和账户号码写到“汇款留言(Remittance Information)”里面。通常3天之后,你登陆自己的账号就能看到钱已经到账了。关于手续费,虽然你在中国的银行办理业务的时候已经交过钱了,但是美国的银行依然会收另一笔手续费。。。这就是美帝。。。大概是20刀。如果你一次汇入超过25000刀,SogoTrade会给让你申请报销汇款产生的手续费:)

第四步:交易

美股交易很容易,填代码,例如GOOG。选择操作,新手一般就用Buy和Sell,然后选择买多少股或者卖多少股。美股是没有“手”的概念的,一股也可以买卖。然后选订单类型,一般选Limit。然后设定一个心理价位。下单。

一些心得:

不要做自己不熟悉的股票。美股没有涨停跌停的概念,不要觉得前一天跌了百分之好几十的股票,第二天能有技术性反弹,虽然真的会有,但是如果你不熟悉那家公司,根本不知道他为什么涨,为什么跌。就好像有一天我看VISN跌了10多%,就买了一些,那两天基本上都是很不安,不知道能不能涨回来。结果是我$1.30买入的,$1.30卖出的。卖出之后两个看,涨到了1.4x。再看又回到了1.3。

如果自己没啥把握,不要尝试买几百块钱玩玩。因为这种玩玩真的没啥意义,1. 浪费钱,每次买入卖出都是要给佣金的。2. 挣钱了你会后悔买的少。3. 亏钱了后悔进场。

大起大落的时候最好不要碰。例如昨天新闻说北京微博要实名制,大家都觉得新浪股价不行了,结果呢?新浪上演大逆转 由早盘跌11%到收盘涨4.26% 。。。

最后,祝大家2012多挣点钱,虽然买不起船票,至少能吃好喝好嘛。

SogoTrade推荐注册地址: https://www.sogotrade.com/s1/NewAccountSetup/Step1.aspx?rf=598726

Python SQLite的使用经验

SQLite是一款轻量级的数据库,很适合用着移动设备上,或者是客户端程序。SQLite的优点有:1. 不需要为数据库起一个单独的进程 2. 整个数据库可以随时拷贝走 3. 不需要任何配置。从Python 2.5开始,SQLite就在标准库了,所以用起来比较方便。下面是我使用过程中的一些使用经验。

连接到数据库?很简单。

import sqlite3
conn = sqlite3.connect('/tmp/sqlite_db')
cur = conn.cursor()

接下来干嘛呢?建一张表吧。这里需要注意的是,SQLite不支持在创建表的同时创建索引,所以要分两步走,先创建表然后再创建索引

create_table_stmt = '''
    CREATE TABLE IF NOT EXISTS test_table (
    id TEXT,
    duration INTEGER,
    event_date TEXT,
    parameter TEXT
);'''
create_index = 'CREATE INDEX IF NOT EXISTS idx_id ON test_table (id);'
cur.execute(create_table_stmt)
cur.execute(create_index)
conn.commit()

然后往里面插一点数据吧,SQLite只支持5种基本的数据类型


NULL. The value is a NULL value.
INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.
REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.
TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
BLOB. The value is a blob of data, stored exactly as it was input.

问题来了,SQLite的时间和日期类型在哪里?原来SQLite可以把时间日期保存在一下几种数据类型里面


TEXT as ISO8601 strings (“YYYY-MM-DD HH:MM:SS.SSS”).
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.

insert_stmt = 'insert into test_table values (?, ?, ?, ?)'
record = ('test', 123, '2011-11-30 12:34:56', 'hello')
cur.execute(insert_stmt, record)
conn.commit()

把日期保存为字符串以后,不能直接拿出来直接当日期用,在用之前要调用SQLite的date函数

例如找前一天存进去的数据:

SELECT
    id,
    duration,
    event_date,
    parameter
FROM test_table
WHERE
    date(event_date) = date('now', '-1 day', 'localtime')
ORDER BY id, event_date

SQLite没有show tables,怎么查询数据库里面有什么表?

SELECT name FROM sqlite_master WHERE type = "table"

在Ubuntu上安装指定版本的Firefox

如果你使用Selenium(Webdriver),并且用Firefox作为一个主要的测试浏览器的话,最近一定比较郁闷。Selenium最近也是一路快跑,升级到了2.12了。他们的博客也就是发到2.9而已。而伟大的FF居然升级到了8.0,伤不起啊!每次新版本Firefox出来以后,Webdriver基本上肯定是支持不了的,然后发一个小版本来fix一下,我说你们这是何苦呢?最郁闷的是,我用PythonBindings,在Ubuntu 11下是用不了的,只能在Ubuntu 10上面搞。昨天Firefox自动升级到8.0,我也就跟着完蛋了。吐槽完毕,说说解决办法。

1. 下载指定版本的Firefox

先在这里:http://releases.mozilla.org/pub/mozilla.org/firefox/releases/下载你想要的Firefox,这里我下的7.0。点进去以后还得选平台,大家都懂得。

2. 解压缩包

tar jxpvf firefox-7.0.tar.bz2

3. 修改owner,假如说上面解压缩出来的文件是放在/home/qa/firefox下

sudo chown -R root:root /home/qa/firefox

4. 把原来的symbol link删掉

sudo unlink /usr/bin/firefox

5. 建一个新的link

sudo ln -s /home/qa/firefox/firefox /usr/bin/firefox

搞定!

最后一步,关掉Firefox的自动更新

1. 在Firefox里面输入

about:config

2. 找到这个变量,设置为false

app.update.auto

终于搞定。

鉴于现在Selenium和Firefox频繁升级,如果大家的自动化测试跑得好好的,就不要主动或者被动地升级了。麻木追新是要付出代价的。

解决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>

删掉。就好了。

WebDriver配置Firefox代理服务器

这玩意儿网上很多,但是坑更多,现在记录一个肯定能用的。

我的环境是Python 2.6 + Selenium 2.6

from selenium import webdriver
profile = webdriver.FirefoxProfile()
profile.set_preference('network.proxy.type', 1)
profile.set_preference('network.proxy.http', 'proxy_url')
profile.set_preference('network.proxy.http_port', 3128)
profile.set_preference('network.proxy.ssl', 'proxy_url')
profile.set_preference('network.proxy.ssl_port', 3128)
profile.update_preferences()
driver = webdriver.Firefox(profile)

这些坑分别是:

有些地方只告诉你配置network.proxy.http和network.proxy.http_port。但是如果不设置network.proxy.type,一切都是浮云。这个配置是个整数,默认是0,就是直接连接;1就是手工配置代理。
profile.set_preference(‘network.proxy.type’, 1)

那个端口号3128是整数
profile.set_preference(‘network.proxy.http’, ‘proxy_url’)
profile.set_preference(‘network.proxy.http_port’, 3128)

如果有些资源是https的,是需要另外配置network.proxy.ssl和network.proxy.ssl_port的。例如facebook的图片。。。

set完以后,是需要update_preferences的。。。
profile.update_preferences()

最后贴一个完整的Firefox配置参数表:http://kb.mozillazine.org/Firefox_:_FAQs_:_About:config_Entries

如果发现自己的配置好像没有生效,那么在webdriver启动的Firefox里面输入about:config。然后对着上面那个配置参数表来看。总会发现掉哪个坑的。

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”>