做GUI自动化测试失败的原因常见的大致有2种:

1、测试元素被修改了,导致被测试元素无法识别;这个是硬伤暂时还没有什么要的方法,看到百度qa有一篇文章说的是关于动态获取客户端元素信息的文章,可能不是适应所有的场景,但是理念还是可以借鉴一下,日后可能会运用到web上来。原文章在这http://qa.baidu.com/blog/?p=206

2、测试过程较复杂导致过程中由于网络等不定因素影响测试结果。如等待时间过长、出现无效页面、出现非预期的窗口;其实这些情况在平时手工操作都没有什么大问题,但是用在自动化过程中,尤其是批量反复执行的过程中就容易在某个用例场景里随机出现。


这里讲到的工具就是针对第2个问题的,提出一种设想即假定一个测试场景需要10个步骤,我们到如果通过gui一步步测试的话就需要依次访问10个页面;而这10个步骤中就有可能某个步骤在测试过程中由于不明原因导致场景失败,即你还没到最终页面就挂了;所以如果有一种方法直接把最后一步的页面返回给浏览器的话,就可以减少这个原因导致的测试失败了。


具体的方案我们团队讨论了好几种,但大概原理都是一样的;即10个页面的访问中把前9个使用代码来完成,把最后获得到的第10个页面的内容直接返回给测试者,这样就跳过前9步在GUI上面的执行,提高了测试的效率,降低了测试不稳定因素。


下面是具体的代码,暂时没有提供设置页面,这里只是原型,后期会在github上更新版本。

main.py

#!/usr/bin/python
#encoding: utf-8
import web
import httplib, urllib

import sys
print sys.getdefaultencoding()
reload(sys)
sys.setdefaultencoding('utf-8')
print sys.getdefaultencoding()

urls = (
'/', 'hello',
'/(.*)', 'seeother',
)

from config import *
from hosts import hosts
web.config.debug = isdebug
app = web.application(urls, globals())

def do_proxy(method, host, path, data=None, headers={}):
print 'request header: %s' % headers
conn = httplib.HTTPConnection(hosts.get(host, host))
conn.request(method, path, body=data, headers=headers)
response = conn.getresponse()
r = [response.status, response.reason, response.read(), response.getheaders()]
print 'response result: %s' % r[:2]
# print 'reseponse body: %s' % r[2]
conn.close()
return r

#import re
#hrefpat = re.compile(r'''href="(/.*?)"''', re.M)
#srcpat = re.compile(r'''src="(/.*?)"''', re.M)
#def replace_short_url(r):
#
# data = r[2]
# if hrefpat.search(data):
# for n in hrefpat.finditer(data):
# nn = 'href="http://%s%s"' % (goal_host, n.group(1))
# data = data.replace(n.group(), nn)
# if srcpat.search(data):
# for n in srcpat.finditer(data):
# nn = 'src="http://%s%s"' % (goal_host, n.group(1))
# data = data.replace(n.group(), nn)
# r[2] = data
# return r

def get_http_request_info(headerappend):
env = web.ctx.env
# print env
request_method = env['REQUEST_METHOD']
request_path = env['REQUEST_URI']
postdata = urllib.urlencode(web.input())
headers = {}
httpkeys = [i for i in env.keys() if i.startswith("HTTP_")]
for k in httpkeys:
headers[k[5:].replace('_', '-').lower()] = env[k]
headers['host'] = goal_host
headers['referer'] = 'http://%s/' % goal_host
headers['accept-encoding'] = 'identity'
if headerappend:
for k,v in headerappend.items():
headers[k] = v
return (request_method, '%s:%s' % (goal_host, env['SERVER_PORT']), request_path, postdata, headers)

from httpstatus import httpstatus

def set_http_response_info(r):
for h in r[3]:
if h[0] != 'cnotent-encoding':
web.header(h[0], h[1])
if h[0] == 'location':
location = h[1]
status = httpstatus.get(r[0])
if r[0] == 200:
return r[2]
elif r[0] == 301:
return status(location)
elif r[0] == 302:
return status(location)
elif r[0] == 303:
return status(location)
elif r[0] == 307:
return status(location)
elif r[0] == 404:
return status(r[2])
elif r[0] >= 500:
return status('%s %s' % r[:2], r[1])
else:
return status()

class seeother:
def GET(self, par):
print 'see other'
http_request_info = get_http_request_info(auth_header)
r = do_proxy(*http_request_info)
return set_http_response_info(r)
def POST(self, par):
return self.GET(par)

class hello:
def GET(self):
http_request_info = get_http_request_info(auth_header)
headers = http_request_info[4]
r = do_proxy("GET", "%s:%s" % (goal_host, 80), "/", "", headers)
return set_http_response_info(r)

if __name__ == "__main__":
app.run()

httpstatus.py


#!/usr/bin/python
#encoding: utf-8
from web import HTTPError
import web

class InternalError(HTTPError):
"""500 Internal Server Error`."""
message = "internal server error"

def __init__(self, status, message=None):
headers = {'Content-Type': 'text/html'}
HTTPError.__init__(self, status, headers, message or self.message)

internalerror = InternalError
httpstatus = {
200 : web.ok,
201 : web.created,
202 : web.accepted,

301 : web.redirect,
302 : web.found,
303 : web.seeother,
304 : web.notmodified,
307 : web.tempredirect,

400 : web.badrequest,
401 : web.unauthorized,
403 : web.forbidden,
404 : web.notfound,
405 : web.nomethod,
406 : web.notacceptable,
409 : web.conflict,
410 : web.gone,
412 : web.preconditionfailed,

500 : internalerror,
501 : internalerror,
502 : internalerror,
503 : internalerror,
504 : internalerror,
505 : internalerror,
}

hosts.py

#!/usr/bin/python
#encoding: utf-8

hosts = {
'login.dangdang.com' : '10.255.254.209',

}

config.py


#!/usr/bin/python
#encoding: utf-8

isdebug = True
auth_header = {} #{"Authorization": "Basic Y2hlbnhpYW93dToxMTExMTE="}
goal_host = "login.dangdang.com"


想要模拟哪个域就把goal_host配置成哪个域,并且把你本机的hosts中这个域的访问地址换成该服务启动的服务器地址。


注意:

切忌服务启动的机器不能和访问该服务的机器是同一台机器;即如果你的服务在机器A上启动的,那么你就不能在机器A上访问该服务,你可以在A之外的其他机器上访问。

因为会出现hosts死循环问题。