【Robot Framework 项目实战 04】基于录制,生成RF关键字及 自动化用例
背景
因为服务的迁移,Jira版本的更新,很多接口文档的维护变少,导致想要编写部分服务的自动化测试变得尤为麻烦,很多服务,尤其是客户端接口需要通过抓包的方式查询参数来编写自动化用例,但是过程中手工重复操作过多,不利于RF用例的快速覆盖,本文给大家介绍如何通过解析抓包拦截的数据,转化为测试关键字并生成测试用例。
实现
抓包
如何安装抓包工具在本文就不赘述了,抓包,过滤出想要的数据,导出,保存的格式注意选择为har:

数据解析
感兴趣的小伙伴可以直接查看导出的har文件内容,它是一个标准的JSON格式的数据,所有的请求数据都在data["log"]["entries"]下。
需要注意的有以下几点,
| 注意点 | 解决方法 |
|---|---|
| 接口返回数据一般使用的base64进行加密 | base64.b64decode() |
| 标准JSON null等参数与Python不一致 | replace("true", "True") |
| method=Get时,request["queryString"] | |
| request["postData"]["text"] | request["postData"]["params"] |
| header中存在多个无需使用的信息,abandon_headers |
根据请求数据生成关键字名称
def gen_filename(url, method):
"""
根据url生成方法名
:param url:
:param method:
:return:
"""
filename = ""
path = str(url).split("/")
# print(path)
# print(len(path))
if len(path) == 2 and method == "GET":
filename = filename + path[1].split("?")[0]
return filename
if len(path) == 2 and method == "DELETE":
filename = filename + path[1].split("?")[0]
return filename
for i in range(len(path)):
if i == 1:
filename = filename + path[i]
if "." in filename:
filename = filename.split(".")[1]
filename = filename + "_"
continue
if i == 2: # 第一个path小写
if "?" in path[i]:
filename = filename + path[i].capitalize().split("?")[0]
break
filename = filename + path[i]
continue
if i == len(path) - 1 and method.upper() == "GET":
filename = filename + path[i].capitalize().split("?")[0]
break
filename = filename + path[i].capitalize()
return filename
完整代码
#! /usr/bin/python
# coding:utf-8
"""
@author:Bingo.he
@file: har_parse.py
@time: 2019/01/01
"""
import os
import json
import xlrd
import copy
import base64
from apitest.Common.Testscript.utils.logger import logger
from xlutils import copy
def save_suits(keyword_filename, datas, file_path, ignore_same_file=None):
"""保存excel数据
:param ignore_same_file:
:param file_path:
:param keyword_filename:
:param datas:
:return:
"""
book = xlrd.open_workbook("source_xls/templates/kw_template.xls", formatting_info=True, encoding_override="utf8")
new_book = copy.copy(book) # 复制读取的Excel
sheet = new_book.get_sheet(0) # 取第一个sheet页
line_num = 1
parameter, value, description, parameter_type, data_type, exp, _type, url, group, documentation, headers, _ = datas
if len(str(exp)) > 30000:
exp = {"data": "返回数据过大"}
sheet.write(line_num, 0, u'%s' % parameter)
sheet.write(line_num, 1, u'%s' % value)
sheet.write(line_num, 2, u'%s' % description)
sheet.write(line_num, 3, u'%s' % parameter_type)
sheet.write(line_num, 4, u'%s' % data_type)
try:
pass
except Exception as e:
logger.error(e)
if isinstance(exp, dict):
pass
else:
exp = str(exp[2:-1])
sheet.write(line_num, 5, u'%s' % eval(json.dumps(str(exp))))
sheet.write(line_num, 6, u'%s' % _type)
sheet.write(line_num, 7, u'%s' % url)
sheet.write(line_num, 8, u'%s' % group)
sheet.write(line_num, 9, u'%s' % documentation)
sheet.write(line_num, 10, u'%s' % headers)
if not os.path.exists(file_path):
os.makedirs(file_path)
if keyword_filename:
target_filename = os.path.abspath(os.path.join(file_path, '{}.xls'.format(keyword_filename)))
if os.path.exists(target_filename) and not ignore_same_file:
raise Exception
new_book.save(target_filename) # 保存修改过后复制的Excel
logger.info("关键字【{}】文件保存成功,保存于【{}】目录".format(keyword_filename, file_path))
class HarParse:
@staticmethod
def get_har_data(har_filename):
"""读取传入的har文件,返回 关键字文件名 及 对应数据 的键值对
:param har_filename:
:return:
"""
with open(har_filename, "r", encoding="utf8") as f:
data = f.readlines()
return json.loads(data[0])["log"]["entries"]
def parse_data(self, har_file, domain_endpoint):
reqs = self.get_har_data(har_file)
xls_datas = {}
for req in reqs:
request = req["request"]
headers_str = self.gen_header_data(request["headers"])
method = request["method"]
url = request["url"].split(domain_endpoint)[1]
resp = req["response"]
base64_content_text = resp["content"]["text"]
try:
resp_text = base64.b64decode(base64_content_text).decode().replace("false", "False").\
replace("null", "None").replace("true", "True")
except Exception as e:
logger.error("请求【{}】method:【{}】返回结果-base64-转化出错".format(request["url"], method))
logger.error("错误原因:【{}】".format(e))
continue
filename = self.gen_filename(url, method)
keys = [i.upper() for i in xls_datas.keys()]
if filename.upper() in keys:
filename = filename + method.upper()
content_type = "urldecode"
if method.upper() == "GET":
url = url.split("?")[0]
query_strs = request["queryString"]
post_data = {}
for query_str in query_strs:
post_data[query_str["name"]] = query_str["value"]
else:
content_type = "json" # application/json
try:
post_data = request["postData"]["text"]
except KeyError:
post_data = request["postData"]["params"]
# request["headers"]
data = ["data", post_data, "", content_type, "", resp_text, method, url, "", self.doc(), headers_str,
request["headers"]]
xls_datas[filename] = data
logger.info("抓取的URL为【{}】".format(request["url"]))
logger.info("获取对应PATH为【{}】".format(url))
logger.info("对应将生成的文件名称为【{}】".format(filename))
logger.info("=============================分割线===============================")
# logger.info(json.dumps(xls_datas.keys(), indent=4, ensure_ascii=False))
return xls_datas
@staticmethod
def gen_filename(url, method):
"""
根据url生成方法名
:param url:
:param method:
:return:
"""
filename = ""
path = str(url).split("/")
# print(path)
# print(len(path))
if len(path) == 2 and method == "GET":
filename = filename + path[1].split("?")[0]
return filename
if len(path) == 2 and method == "DELETE":
filename = filename + path[1].split("?")[0]
return filename
for i in range(len(path)):
if i == 1:
filename = filename + path[i]
if "." in filename:
filename = filename.split(".")[1]
filename = filename + "_"
continue
if i == 2: # 第一个path小写
if "?" in path[i]:
filename = filename + path[i].capitalize().split("?")[0]
break
filename = filename + path[i]
continue
if i == len(path) - 1 and method.upper() == "GET":
filename = filename + path[i].capitalize().split("?")[0]
break
filename = filename + path[i].capitalize()
return filename
@staticmethod
def gen_header_data(headers):
headers_str = ""
for i in headers:
abandon_headers = ["Host", "User-Agent", "Accept-Encoding", "Accept", "Connection", "Content-Length"]
if i["name"] in abandon_headers:
continue
headers_str = headers_str + i["name"] + "=" + i["value"] + " "
return headers_str
@staticmethod
def doc():
return """
... 【功能】
...
... 【参数】
... url: 请求域名
... data: 请求参数
...
... 【返回值】
... Ret: response对象
"""
【Robot Framework 项目实战 04】基于录制,生成RF关键字及 自动化用例的更多相关文章
- 【Robot Framework 项目实战 03】使用脚本自动生成统一格式的RF自动化用例
背景 虽然大家都已经使用了统一的关键字,但是在检查了一些测试用例之后,还是发现因为大家对RF的熟悉程度不一导致的测试用例颗粒度差异很大的情况:而且在手动方式转化测试用例过程中,有不少工作是完全重复的且 ...
- 【Golang】基于录制,自动生成go test接口自动化用例
背景 之前写过一篇博客,介绍怎么用Python通过解析抓包数据,完成自动化用例的编写.最近这段时间在使用go test,所以就在想能不能也使用代码来生成自动化用例,快速提升测试用例覆盖率.说干就干. ...
- 【Robot Framework 项目实战 02】使用脚本生成统一格式的RF关键字
背景 在微服务化的调用环境下,测试数据及接口依赖的维护是一个问题,因为依赖的接口和数据可能不在同一个服务下,而这相关的多个服务往往是不同人员来测试的. 因此为了节省沟通成本,避免关键字的重复冗余.所以 ...
- 【Robot Framework 项目实战 01】使用 RequestsLibrary 进行接口测试
写在前面 本文我们一起来学习如何使用Robot Framework 的RequestsLibrary库,涉及POST.GET接口测试,RF用例分层封装设计等内容. 接口 接口测试是我们最常见的测试类型 ...
- 【Robot Framework 项目实战 00】环境搭建
前言 我们公司在推广RF这个框架做后端接口测试,力求让同事们能更快的完成服务端需求的自动化,作为主导者之一,决定分享一些经验,方便后来者. 我会从安装部署.Request.selenium.自定义框架 ...
- 【Robot Framework 项目实战 02】SeleniumLibrary Web UI 自动化
前言 SeleniumLibrary 是针对 Robot Framework 开发的 Selenium 库.它也 Robot Framework 下面最流程的库之一.主要用于编写 Web UI 自动化 ...
- 自动化测试框架Cucumber和Robot Framework的实战对比
自动化测试框架Cucumber和RobotFramework的实战对比 一.摘要 自动化测试可以快速自动完成大量测试用例,节约巨大的人工测试成本:同时它需要拥有专业开发技能的人才能完成开发,且需要大量 ...
- SDKStyle的Framework项目使用旧版项目文件生成的Nuget包遇到的问题
随笔-2021-11-10 SDKStyle的Framework项目使用旧版项目文件生成的Nuget包遇到的问题 简介 C#从NetCore之后使用了新版的项目文件,SDK-Style项目,新版本的项 ...
- Robot Framework 项目搭建
首先新建一个项目“RobotDemo".项目Type一般选择“Directory”形式. 项目第一层可以放3种文件:Test Suite.Directory 和 Resource File. ...
随机推荐
- 解决wpscan无法更新
如果wpscan无法更新的话 一般的原因都是源或者更新地址无法访问 下面解决 updatedb #先更新一下系统的索引 locate wpscan #定位到wpscan的目录 大概就是updater. ...
- 6.NIO2-Path、Paths、Files
NIO.2 jdk1.7中,java对 NIO 极大的扩展,主要增强的是对文件处理 和 文件系统特性的支持 关于其中一些API的使用 public class TestNIO_2_Path_File ...
- java利用MultipartRequest的getFileName方法不能得到原文件名问题
想利用MultipartRequest的getFileName方法来一次获取多个上传的文件名字时,得到的不是文件的名字,而是 input 的name属性 最后找到了答案,解决方法,参照http://s ...
- 补充:HTML标签和CSS
角标标签: 上角标:sup 下角标:sub <!DOCTYPE html> <html> <head> <meta charset="UTF-8&q ...
- 通用mapper接口已经写好的 根据 list 集合查询 相应数据
package tk.mybatis.mapper.additional.idlist; import org.apache.ibatis.annotations.Param; import org. ...
- Django hrf:权限、频率控制
一.权限 二.频率控制 一.权限 1.权限介绍 只有超级用户才能访问指定的数据,所以就要用权限组件进行设置 2.局部使用 # 单独抽出写一个视图类 from rest_framework.permis ...
- jquery中的ajaxSetup
在项目开发中如果我们想给某一个页面中的所有的ajax设置统一的参数的情况下,可以是使用ajaxSetup,非常好用 $.ajaxSetup({ type:'post', dataType:'json' ...
- D. Lakes in Berland (DFS或者BFS +连通块
https://blog.csdn.net/guhaiteng/article/details/52730373 参考题解 http://codeforces.com/contest/723/prob ...
- BCB 编写服务程序的一个注意事项
BCB编写服务,install报错的一个问题 今天编写了一个服务,最后INSTALL 的时候报错,如图: 经过近1小时的比较(俺过去写例子),居然无意中设置了一个属性 ...
- 【C++/html版 代码 : 暴力破解数字红包 】-- 只要有编译器或者,不看运气,用手速敲代码说话,多人合作效果更佳!
需求分析: 或者是更大的范围! 是不是很捉急!运气背点不就over了! C++版: #include <stdio.h> #include <stdlib.h> #includ ...