自动化脚本是交给工具去执行,有时候打印的错误信息并不十分明确,如果在脚本执行出错的时候将

对当前窗口截图保存,那么通过图片信息会更只观帮助我们找到脚本出错的原因。Webdriver 提供了截图

函数 get_screenshot_as_file()来截取当前窗口。

baidu.py

#coding=utf-8

from selenium import webdriver

driver = webdriver.Chrome()

try:

driver.find_element_by_id('kw_error').send_key('selenium')

driver.find_element_by_id('su').click()

except :

driver.get_screenshot_as_file("D:\\baidu_error.jpg")

driver.quit()

在本例中用到了 Python 的异常处理,要本例中百度输入框的 id=kw_error 会定位不到元素,那么 try

就会捕捉到这个异常,从而执行 except,在 except 中执行 get_screenshot_as_file()对当前窗口进行截图,这

里需要指定图片的保存路径及文件名,并且关闭当前驱动。

脚本运行完成打开 D 盘就可以找到 baidu_error.jpg 图片文件了。

关闭窗口

在前页的例子中我们一直在使用 quit()方法,其含义为退出相关的驱动程序和关闭所有窗口。除此之

外 WebDriver 还提供了 close()方法,用于关闭当前窗口。当脚本在执行时打开了多个窗口,如本章的第 10

小节多窗口的处理,这个时候只能关闭其中的某一个窗口,这个时候就需要使用 close()来关闭。

验证码的处理

对于 web 应用来说,大部分的系统在用户登录时都要求用户输入验证码,验证码的类型的很多,有字

母数字的,有汉字的,甚至还要用户输入一条算术题的答案的,对于系统来说使用验证码可以有效果的防

止采用机器猜测方法对口令的刺探,在一定程度上增加了安全性。

但对于测试人员来说,不管是进行性能测试还是自动化测试都是一个比较棘手的问题。在 WebDriver

中并没有提供相应的方法来处理验证码。这里我根据自己的经验来谈谈处理验证码的几种常见方法。

对验证码的常见处理方式有以下几种。

去掉验证码

这是最简单的方法,对于开发人员来说,只是把验证码的相关代码注释掉即可,如果是在测试环境,

这样做可省去了测试人员不少麻烦,如果自动化脚本是要在正式环境跑,这样就给系统带来了一定的风险。

设置万能码

去掉验证码的主要是安全问题,为了应对在线系统的安全性威胁,可以在修改程序时不取消验证码,

而是程序中留一个“后门”---设置一个“万能验证码”,只要用户输入这个“万能验证码”,程序就认为

验证通过,否则按照原先的验证方式进行验证。

设计万能码的方式非常简单,只对于用户的输入信息多加一个逻辑判断,如下面的小例子:

baidu.py

#coding=utf-8

import random

#生成一个 1000 到 9999 之间的随机整数

verify = random.randint(1000,9999)

print u"生成的随机数:%d " %verify

number = input(u"请输入随机数:")

print number

if number == verify:

print u"登录成功!!"

elif number == 132741:

print u"登录成功!!"

else:

print u"验证码输入有误!"

randint()用于生成随机数,设置随机数的范围为1000~9999之间。运行程序分别输入正确的验证码、万

能码和错误的验证码,执行结果如下:

Python Shell

>>> ================================ RESTART ================================

>>>

生成的随机数:8396

请输入随机数:8396

8396

登录成功!!

>>> ================================ RESTART ================================

>>>

生成的随机数:5113

请输入随机数:132741

132741

登录成功!!

>>> ================================ RESTART ================================

>>>

生成的随机数:1996

请输入随机数:1234

1234

验证码输入有误!

验证码识别技术

例如可以通过 Python-tesseract 来识别图片验证码,Python-tesseract 是光学字符识别 Tesseract OCR 引

擎的 Python 封装类。能够读取任何常规的图片文件(JPG, GIF ,PNG , TIFF 等)。不过,目前市面上的验证码

形式繁多,目前任何一种验证码识别技术,识别率都不是100% 。

记录 cookie

通过向浏览器中添加 cookie 可以绕过登录的验证码,这是比较有意思的一种解决方案。比如我们在

第一次登录某网站可以勾选“记住密码”的选项,当下次再访问该网站时自动就处于登录状态了。这样其

实也绕过验证码问题。那么这个“记住密码”的功能其实就记在了浏览器的 cookie 中。前面已经学了通过WebDriver 来操作浏览器的 Cookie,可以通过 add_cookie()方法将用户名密码写入浏览器 cookie ,再次访

问网站时服务器直接读取浏览器 Cookie 登录。

Python Shell

....

#访问 xx 网站

driver.get("http://www.xx.cn")

#将用户名密码写入浏览器 cookie

driver.add_cookie({'name':'Login_UserNumber', 'value':'username'})

driver.add_cookie({'name':'Login_Passwd', 'value':'password'})

#再次访问 xx 网站,将会自动登录

driver.get("http://www.xx.cn/")

time.sleep(3)

....

driver.quit()

这种方式最大的问题是如何从浏览器的 Cookie 中找到用户名和密码对应的 key 值,并传传输入对应的

登录信息。可以 get_cookies()方法来获取登录的所有的 cookie 信息,从中找到用户名和密码的 key。当然,

如果网站登录时根本不将用户名和密码写 Cookie,这会存在一定的安全风险。那么这种方式就不起作用了。

WebDriver 原理

WebDriver 是按照 server – client 的经典设计模式设计的。

server 端就是 remote server,可以是任意的浏览器。当我们的脚本启动浏览器后,该浏览器就是 remote

server,它的职责就是等待 client 发送请求并做出相应。

client 端简单说来就是我们的测试代码,我们测试代码中的一些行为,比如打开浏览器,转跳到特定

的 url 等操作是以 http 请求的方式发送给被 测试浏览器,也就是 remote server;remote server 接受请求,

并执行相应操作,并在 response 中返回执行状态、返回值等信息。

webdriver 的工作流程:

1. WebDriver 启动目标浏览器,并绑定到指定端口。该启动的浏览器实例,做为 WebDriver 的 remote

server。

2. Client 端通过 CommandExcuter 发送 HTTPRequest 给 remote server 的侦听端口(通信协议: the

webriver wire protocol)

3. Remote server 需要依赖原生的浏览器组件(如:IEDriverServer.exe、chromedriver.exe),来转化转

化浏览器的 native 调用。

在 Python 提供了 logging 模块,logging 模块给运行中的应用提供了一个标准的信息输出接口。它提供

了 basicConfig()方法用于基本信息的定义。将 debug 模块开启。就可以捕捉到客户端与服务器的交互信息。

test.py

#coding=utf-8

from selenium import webdriver

import logging

logging.basicConfig(level=logging.DEBUG)

diver = webdriver.Firefox()

diver.find_element_by_id("kw").send_keys("selenium")

diver.find_element_by_id("su").click()

diver.quit()

运行脚本,basicConfig()所捕捉的 log 信息。不过 basicConfig()开启的 debug 模式只能捕捉到客户端向

服务器所发送的 POST 请求,而无法获取服务器所返回应答信息。我们在后面的章节中将会用 Selenium

Server,通过 Selenium Server 将会获取到更详细请求与应答信息。

Python Shell

>>> ================================ RESTART ================================

>>>

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session {"desiredCapabilities": {"platform": "ANY",

"browserName": "firefox", "version": "", "javascriptEnabled": true}}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601/url

{"url": "http://www.baidu.com", "sessionId":

"0f0d51f5-affc-4af0-9c45-4b3c4931c601"}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601/elem

ent {"using": "id", "sessionId": "0f0d51f5-affc-4af0-9c45-4b3c4931c601",

"value": "kw"}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601/elem

ent/{12722a5d-58f3-457c-ad5e-348b230c6f6a}/value {"sessionId":

"0f0d51f5-affc-4af0-9c45-4b3c4931c601", "id":

"{12722a5d-58f3-457c-ad5e-348b230c6f6a}", "value": ["s", "e", "l", "e", "n", "i",

"u", "m"]}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601/elem

ent {"using": "id", "sessionId": "0f0d51f5-affc-4af0-9c45-4b3c4931c601",

"value": "su"}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:POST

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601/elem

ent/{8090ac84-2d92-4b48-8320-dadcfbf15f40}/click {"sessionId":

"0f0d51f5-affc-4af0-9c45-4b3c4931c601", "id":

"{8090ac84-2d92-4b48-8320-dadcfbf15f40}"}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

DEBUG:selenium.webdriver.remote.remote_connection:DELETE

http://127.0.0.1:34229/hub/session/0f0d51f5-affc-4af0-9c45-4b3c4931c601

{"sessionId": "0f0d51f5-affc-4af0-9c45-4b3c4931c601"}

DEBUG:selenium.webdriver.remote.remote_connection:Finished Request

总结:

虽然,本章花了20小节来讲元素的定位与操作,对于碰到的一些常见功能,如何通过技巧来定位它们,

但是在实际的自动化脚本开发中,不管是新手还是具有一定经验的老手,所遇到最多的问题仍然是元素的

定位。

有时元素定位非常简单,例如,我们只要知道这个元素有的 id 和 name 就可以轻松的来定位到它;有

时元素的定位却非常的令人头疼,尽管我们想尽了办法,仍然无法定位到它。在这里笔者也没万能的方法

来帮你解决这些实际问题。

对于不同的 web 项目,所用到的前端技术也不同,有些项目会用到 EXT(一个强在的 js 类库),有

些会用到 AJAX(一种创建交互式网页应用的网页开发技术),这些技术的应用无疑对于前端开发人员可

以快速的生成所需要的页面,但对于 UI 自动化测试人员来说,增加了定位页面元素的难度。

所以,在进行项目实现 UI 自动化评估的时候,页面元素的定位难度也是一个评估标准,如果处处都

是很难定位的元素,那么无疑会增加脚本的开发与维护的成本,得不偿失。这个时候我可以考虑将更新多

的精力放在单元或接口层的自动化上。

对于自动化测试人员来说,如果熟悉前端技术也会大大降低你定位元素的难度,熟练使用 XPath 和

CSS 技术会使你的定位变得容易很多,如果精通 javascript、jquery 等技术,那么使你的定位之路变得更加

随心所欲。

在我们尝试开展自动化的 web 项目中,大多数在设计初期并没有考虑是否易于进行自动化,所以更多

的会以实现功能为目的,这个也是后期开展自动化困难重重的重要原因。如果开发人员在设计代码的时候就考虑是否容易自动化,为必要的元素加上 id 和 name 属性的话,那么我们自动化工作会变得容易很多。

测试人员如何更顺利的实施自动化测试工作。一方面要努力学好技术,克服技术难题。另一方面,我

们要清楚的认识到,自动化技术的应用与实践不是一个人的战斗。一定要得到整个团队的配合与支持。

当然,站在公司的立场,不能带来收益的事情是很难得到支持的,这个就需要读者去综合评估目前的

产品真的是否适合引入自动化,或者目前的阶段是否真的迫切需要开展自动化。

假如,你已经动手开始进行自动化了,笔者再提几点建议。

1、熟练掌握 xpath\CSS 定位的使用,这样在遇到各种难以定位的属性时才不会变得束手无策。

2、准备一份 Slenium Python Bindings,及时查阅 WebDriver 所提供的方法。

3、学习掌握 JavaScript 语言,掌握 JavaScript 好处前面已经有过阐述,可以让我们的自动化测试工作

更加游刃有余。

4、自动化测试归根结底是与前端打交道,多多熟悉前端技术,如http请求,HTML语言 ,cookie、session

机制等。