【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. ...
随机推荐
- 使用Java Executor框架实现多线程
本文将涵盖两个主题: 通过实现Callable接口创建线程 在Java中使用Executor框架 实现Callable接口 为了创建一段可以在线程中运行的代码,我们创建了一个类,然后实现了Callab ...
- python常见函数运用【一】
1.Python hasattr() 函数 描述hasattr() 函数用于判断对象是否包含对应的属性. 语法 hasattr 语法: hasattr(object, name)参数object -- ...
- js数组破坏性和非破坏性方法
数组原型方法:破坏性.会改变数组. shift().unshift().pop().push().splice();resver(),sort().在对数字排序的时候不能用原来的方法了,那样会导致值溢 ...
- 2.Java集合-ConcurrentHashMap实现原理及源码分析
一.为何用ConcurrentHashMap 在并发编程中使用HashMap可能会导致死循环,而使用线程安全的HashTable效率又低下. 线程不安全的HashMap 在多线程环境下,使用HashM ...
- ASE —— 第二次结对作业
目录 重现基线模型 基线模型原理 模型的优缺点 模型重现结果 提出改进 改进动机 新模型框架 评价合作伙伴 重现基线模型 基线模型原理 我们选用的的模型为DeepCS,接下来我将解释一下它的原理. 我 ...
- [LeetCode] 22. 括号生成 ☆☆☆(回溯)
描述 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", "(()( ...
- 配置Python、Django环境变量教程
配置环境变量 在Windows下你必须配置环境变量! 右击桌面或者你能看到的任何 此电脑.这台电脑或者我的电脑. 右击:属性 点击:高级系统设置 点击:环境变量 找到系统变量下的Path,双击 点击新 ...
- 【异常】ser class threw exception: java.sql.SQLException: The last packet successfully received from the server was 39,444 milliseconds ago. The last
1 详细异常 ser class threw exception: java.sql.SQLException: The last packet successfully received from ...
- PAT Basic 1055 集体照 (25 分)
拍集体照时队形很重要,这里对给定的 N 个人 K 排的队形设计排队规则如下: 每排人数为 /(向下取整),多出来的人全部站在最后一排: 后排所有人的个子都不比前排任何人矮: 每排中最高者站中间(中间位 ...
- HRNet网络结构
最近正在阅读CVPR2019的论文Deep High-Resolution Representation Learning for Human Pose Estimation. 无奈看论文中的Netw ...