通用技术 通过 Har 生成测试脚本 (LR 为例)

among · June 27, 2016 · Last by Lancats replied at September 05, 2016 · 3410 hits
本帖已被设为精华帖!

前言

HTTP Archive (HAR) format 是http协议交互的归档格式。
这个格式在本质上就是utf8格式的json字符串,存储着所有的http请求和http响应的数据,包括所有的head和body。

如何获取HAR格式

一般,很多proxy类的工具,如fiddler,charles,原来一直以为charles不支持保存为har格式,后来才知道是在 export 菜单里面:

通过代理和反向代理获取http报文

在charles中,支持代理,反向代理,端口转发 这三种主要的方法获取交互的报文。

  1. 代理模式:这个就是普通的代理,proxy模式,浏览器都支持。
  2. 反向代理:简单说就是代理服务器,对于不支持设置代理的应用,如接口类,可以通过这个来获取报文。
  3. 端口转发:这个功能更强大,基于tcp,udp层的,对于Socket类的都能录到报文。一般如果不知道是什么协议的, 可以用这个,如果判断是http协议的, 最好用反向代理模式的,这样可以更直观的看到解析后的报文。

解析har

通过代理和反向代理的方式,可以获取到http报文,导出为har格式后,进行解析,可以直接生成测试脚本,以生成loadrunner 脚本为例。
基于python的脚本如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from optparse import OptionParser
import os
import sys
import json

# check env
if sys.version_info < (3, 4):
raise RuntimeError('At least Python 3.4 is required.')

restype = ('js', 'css', 'jpg', 'gif', 'ico', 'png')

def list2dic(headers):
header_dic = dict()
for head in headers:
if head['name'] in header_dic:
header_dic[head['name']] = header_dic[head['name']] + ',' + head['value']
else:
header_dic[head['name']] = head['value']
return header_dic

def dictoand(dct):
res_list = list()
for tp in dct:
res_list.append('%s=%s' % (tp['name'], tp['value']))
return '&'.join(res_list)

def dict2lr(lrsc):
tmpl = '''
web_custom_request("%(name)s",
"URL=%(url)s",
"Method=%(method)s",
"Resource=%(res)s",
"Referer=%(referer)s",
"EncType=%(enctype)s",
"Body=%(body)s",
LAST);'''
# url
url = lrsc['url']
method = lrsc['method']
name = url.split('/')[-1]
name = name.split('?')[0]
suff = url.split('.')[-1]
# Resource type
global restype
res = '0'
if suff in restype:
res = '1'

# Content-Type
enctype = ''
if 'Content-Type' in lrsc:
enctype = lrsc['Content-Type']
# Referer
referer = ''
if 'Referer' in lrsc:
referer = lrsc['Referer']

# Body
body = ''
if 'posttext' in lrsc:
body = lrsc['posttext']
elif 'postparams' in lrsc:
body = dictoand(lrsc['postparams'])
body = body.replace('"', '\\"')
res = tmpl % {'name': name, 'url': url, 'method': method, 'enctype': enctype, 'referer': referer, 'res': res,
'body': body}
# Head
if 'SOAPAction' in lrsc:
res = ("\n" + ' web_add_header("SOAPAction", "%s")' + ";\n" + res) % lrsc['SOAPAction']
return res

def parhar(harfile):
res = list()
try:
FH = open(harfile, mode='r', encoding='utf-8', closefd=True)
all = json.load(FH)
FH.close()
except Exception as ex:
print('Open har file errr: %s' % ex)
quit()

har_ver = all['log']['version']
creater = all['log']['creator']['name']
entries = all['log']['entries']
ct = len(entries)
for et in entries:
stm = et['startedDateTime']
req = et['request']
rsp = et['response']
lrsc = dict()
if '_charlesStatus' in rsp and rsp['_charlesStatus'] != 'Complete':
continue
lrsc['method'] = req['method']
lrsc['url'] = req['url']
headers = req['headers']
# http head
header_dic = list2dic(headers)
if 'SOAPAction' in header_dic:
lrsc['SOAPAction'] = header_dic['SOAPAction'].replace('"', '\\"')
if 'Referer' in header_dic:
lrsc['Referer'] = header_dic['Referer']
if 'Content-Type' in header_dic:
lrsc['Content-Type'] = header_dic['Content-Type']
if lrsc['method'] == 'GET':
pass
elif lrsc['method'] == 'POST':
if 'postData' in req:
if 'text' in req['postData']:
lrsc['posttext'] = req['postData']['text']
if 'params' in req['postData']:
lrsc['postparams'] = req['postData']['params']
if 'mimeType' in req['postData']:
lrsc['postmime'] = req['postData']['mimeType']
else:
continue
res.append(dict2lr(lrsc))
return res

if __name__ == '__main__':
parse = OptionParser()
parse.add_option("-f", action="store", dest="harfile", help='harfile path')
parse.add_option("-o", action="store", dest="lrfile", help='action.c path')
(options, args) = parse.parse_args()

if options.harfile is None or options.lrfile is None:
parse.print_help()
quit()
if not os.path.exists(options.harfile):
print('Har file %s not exist' % options.harfile)
quit()
res = parhar(options.harfile)
file = open(options.lrfile, mode='w', encoding='utf-8')
for sc in res:
file.write(sc)
file.write("\n")
file.close()
print('Output to %s' % options.lrfile)

生成的脚本,可以直接copy到LR中使用,根据需要做参数化和关联,对于不支持通过lr录制的,如接口类,app类的,通过这种方式可以更快的生成脚本。
可能还存在部分未考虑到的问题,如请求并发,编码等问题。需要根据实际情况调整。

其他

对于其他工具,也可以自己解析har生成对应的测试案例、脚本等。
另外,在charles中,还支持web interface,开启后,可以远程管理charles,打开或关闭选项,下载har文件,有需要的可以自己写请求实时远程管理,做到进一步的自动化。
有需要就自己折腾吧。

共收到 6 条回复 时间 点赞

单纯为了方便录制app的http请求,可以直接通过代理LR录制;楼主方法使用很多场景,赞.
Charles还可以这么玩,受教了.

恒温 将本帖设为了精华贴 28 Jun 03:15

赞!
之前针对LR有时候无法录制的情况,也做了一个类似的,可以将Fiddler2录制生成的文件转换为LR脚本。
https://github.com/debugtalk/Fiddler2LRScript

among #4 · July 12, 2016 作者

#3楼 @debugtalk 用熟了charles,很早就想直接解析charles保存的chls格式的文件,发现格式太复杂,也是后来才知道在export里面可以直接导出为har格式。

配合charles的web interface接口,可折腾空间很大。

赞一个,不错,可以尝试下。

不错,不过LR已经支持直接打开HAR,自动转换成LR的脚本的。

赞一个 不错

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up