背景

在微服务化的调用环境下,测试数据及接口依赖的维护是一个问题,因为依赖的接口和数据可能不在同一个服务下,而这相关的多个服务往往是不同人员来测试的。

因此为了节省沟通成本,避免关键字的重复冗余。所以我们在RF框架推广之初就确定了接口关键字统一管理的基调,方便不同服务之间的调用。

脚本介绍

中间数据Excel

为了让关键字的格式统一,我们每一个业务线使用同一个关键字数据生成脚本,小伙伴们通过维护Excel来维护接口关键字。

被维护的Excel内容如下:

Excel文件名称规则: 为接口服务名称和path的组合,

Excel文件内容规则: 接口传递参数类型,请求参数,请求方式,path路径,预期返回,接口headers及接口描述等信息。

初期,关键字的生成由一人统一管理,发送数据及预期结果,主要用于检测数据

对应生成的关键字如下:



注:Excel中的Path及headers中存在${}时,关键字会将其作为传入参数

关键字命名规则: 公司代号_流水线(项目组)代号_服务名称_接口path(path相同时以请求方式区分)

完整脚本

具体的脚本较为简单: apitest/Common/Testscript/common/gen_rf_kw.py

#! /usr/bin/python
# coding:utf-8
"""
@author:Bingo.he
@file: gen_common_kw.py
@time: 2019/01/01
"""
import os
import re
from apitest.Common.Testscript.utils.logger import logger
from apitest.Common.Testscript.utils.operate_xls import OperateXls class GenRFkw:
def __init__(self, xls_folder, demo_case_folder):
self.xls_folder = xls_folder
self.target_case_folder = demo_case_folder @staticmethod
def kw_requests_init(target_robot_name):
with open(target_robot_name, 'a') as f:
f.write('*** Settings ***' + '\n')
f.write('Library TestLibrary' + '\n')
f.write('Library RequestsLibrary' + '\n')
f.write('\n')
f.write('*** Keywords ***' + '\n') @staticmethod
def gen_kw(target_robot_name, param_type, req_method, url, document, headers, msg_type):
if param_type == "json" or param_type == "data":
# param_type = "data"
param_type = "json"
with open(target_robot_name, 'a', encoding="utf-8") as f:
f.write(' [Arguments] ${url}')
f.write(' ${' + param_type + '}') # 允许url中传递可变参数
if "${" in url:
p = re.compile(r'{(.*?)}')
verify_urls = p.findall(url)
for verify_url in verify_urls:
f.write(" ${{{}}}".format(verify_url)) # 允许headers中传递可变参数
if "${" in headers:
sprint_str = headers
p = re.compile(r'{(.*?)}')
sprint_num = p.findall(sprint_str)
for s in sprint_num:
f.write(" ${{{}}}".format(s))
f.write('\n') f.write(' [Documentation] ' + document + '\n') # 兼容form-data请求
if "multipart/form-data" == msg_type:
f.write(' ${boundary}= xl boundary parse ${data}' + '\n') f.write(' ${headers} Create Dictionary ' + headers + '\n')
f.write(' Create Session api ${url} ${headers} verify=${False}' + '\n')
if '' != param_type.strip(): # 接口输入参数个数不为零
if req_method.upper() == 'GET' or req_method.upper() == 'DELETE':
f.write(' ${{Ret}} {} Request api '.format(req_method.capitalize()) + url)
f.write('${' + param_type + '}') # 发送GET请求,直接把EXCEL中读取出来的参数连接到URL后面
f.write('\n')
else:
f.write(' ${{Ret}} {} Request api '.format(req_method.capitalize()) + url)
f.write(' ' + param_type + '=${' + param_type + '}')
f.write('\n')
f.write(' [Return] ${Ret}' + '\n')
f.write('\n') @staticmethod
def find_file_name(file_dir):
files = None
for root, dirs, files in os.walk(file_dir):
logger.info("当前目录路径" + root) # 当前目录路径
logger.info("当前路径下所有子目录" + str(dirs)) # 当前路径下所有子目录
logger.info("当前路径下所有非目录子文件" + str(files)) # 当前路径下所有非目录子文件
files = [file for file in files if ".xls" in file]
# 按照顺序排序
files.sort()
return files @staticmethod
def interface_name(kw_excel):
return kw_excel.split(".")[0] def run(self, **kwargs):
xls_files = self.find_file_name(self.xls_folder)
target_robot_name = os.path.join(self.target_case_folder, "DT_Hb_kwRequests.robot") if os.path.exists(target_robot_name):
os.remove(target_robot_name) self.kw_requests_init(target_robot_name)
for xls_file in xls_files:
interface_name = self.interface_name(xls_file)
with open(target_robot_name, 'a') as f:
f.write('DT_Hb_' + interface_name + '\n')
book = OperateXls(os.path.join(self.xls_folder, xls_file), index=0)
param_type = book.rf_xls_msg_type()
# param = book.rf_xls_param_value()
decode_type = book.rf_xls_msg_type()
url = book.rf_xls_url()
document = book.rf_xls_document()
headers = book.rf_xls_headers()
method = book.rf_xls_method()
self.gen_kw(target_robot_name, param_type, method, url, document, headers, decode_type)

依赖脚本: apitest/Common/Testscript/utils/operate_xls.py

#! /usr/bin/python
# coding:utf-8
"""
@author:Bingo.he
@file: operate_xls.py
@time: 2019/01/01
"""
import xlrd
from apitest.Common.Testscript.utils.logger import logger class OperateXls:
def __init__(self, xls_ile, index):
self.book = xlrd.open_workbook(xls_ile, encoding_override='utf-8')
self.sheet = self.book.sheet_by_index(index) # 通过sheet索引获得sheet对象 def switch_sheet_index(self, count):
"""
通过index切换sheet对象
:param count:
:return:
"""
self.sheet = self.book.sheet_by_index(count) def switch_sheet_by_name(self, sheet_name):
"""
通过sheet name 切换 sheet对象
:param sheet_name:
:return:
"""
self.sheet = self.book.sheet_by_name(sheet_name) def sheet_name_by_index(self, count):
return self.book.sheet_names()[count] # 获得指定索引的sheet表名字 def get_value(self, rowx, colx):
return self.sheet.cell_value(rowx, colx) def sheet_params_by_name(self, sheet_name):
"""
返回sheet页所有数据
:param sheet_name:
:return: sheet data format: {row_num:[data of column1,data of column2....]}
"""
all_data = {}
row_data = []
sheet = self.book.sheet_by_name(sheet_name) # 通过sheet名字来获取
row_num = sheet.nrows # 获取行总数
cols_num = sheet.ncols # 获取行总数
logger.info("有效数据行数: " + str(row_num))
logger.info("有效数据列数: :" + str(cols_num))
for i in range(0, row_num):
for c in range(cols_num):
row_data.append(sheet.cell_value(i, c)) # 获取指定EXCEL文件中,第一个SHEET中的接口字段名
all_data[i] = row_data
row_data = []
# logger.info(str(json.dumps(all_data, indent=4)))
logger.info(str(all_data))
return all_data def sheet_params_by_index(self, index):
"""
返回sheet页所有数据
:param index:
:return: sheet data format: {row_num:[data of column1,data of column2....]}
"""
all_data = {}
row_data = []
sheet = self.book.sheet_by_index(index) # 通过sheet名字来获取
row_num = sheet.nrows # 获取行总数
cols_num = sheet.ncols # 获取行总数
logger.info("有效数据行数: " + str(row_num))
logger.info("有效数据列数: :" + str(cols_num))
for i in range(0, row_num):
for c in range(cols_num):
row_data.append(sheet.cell_value(i, c)) # 获取指定EXCEL文件中,第一个SHEET中的接口字段名
all_data[i] = row_data
row_data = []
# logger.info(str(json.dumps(all_data, indent=4)))
logger.info(str(all_data))
return all_data class ReadRFExcel(OperateXls):
def rf_xls_param_type(self):
return self.get_value(1, 0) # 获取'B2'字段内容 def rf_xls_param_value(self):
return self.get_value(1, 1) # 获取'B2'字段内容 def rf_xls_msg_type(self):
return self.get_value(1, 3) # 获取'D2'字段内容 def rf_xls_method(self):
return self.get_value(1, 6) # 获取'G2'字段内容 def rf_xls_url(self):
return self.get_value(1, 7) # 获取'H2'字段内容 def rf_xls_group(self):
return self.get_value(1, 8) # 获取'I2'字段内容 def rf_xls_document(self):
return self.get_value(1, 9) # 获取'J2'字段内容 def rf_xls_headers(self):
return self.get_value(1, 10) # 获取'K2'字段内容 if __name__ == '__main__':
op = ReadRFExcel("test.xls", 0)
print(op.rf_xls_headers())
op.switch_sheet_index(1)
op.rf_xls_headers()
op.sheet_params_by_index(1)
op.sheet_name_by_index(1)

【Robot Framework 项目实战 02】使用脚本生成统一格式的RF关键字的更多相关文章

  1. 【Robot Framework 项目实战 02】SeleniumLibrary Web UI 自动化

    前言 SeleniumLibrary 是针对 Robot Framework 开发的 Selenium 库.它也 Robot Framework 下面最流程的库之一.主要用于编写 Web UI 自动化 ...

  2. 【Robot Framework 项目实战 03】使用脚本自动生成统一格式的RF自动化用例

    背景 虽然大家都已经使用了统一的关键字,但是在检查了一些测试用例之后,还是发现因为大家对RF的熟悉程度不一导致的测试用例颗粒度差异很大的情况:而且在手动方式转化测试用例过程中,有不少工作是完全重复的且 ...

  3. 【Robot Framework 项目实战 00】环境搭建

    前言 我们公司在推广RF这个框架做后端接口测试,力求让同事们能更快的完成服务端需求的自动化,作为主导者之一,决定分享一些经验,方便后来者. 我会从安装部署.Request.selenium.自定义框架 ...

  4. 【Robot Framework 项目实战 01】使用 RequestsLibrary 进行接口测试

    写在前面 本文我们一起来学习如何使用Robot Framework 的RequestsLibrary库,涉及POST.GET接口测试,RF用例分层封装设计等内容. 接口 接口测试是我们最常见的测试类型 ...

  5. 【Robot Framework 项目实战 04】基于录制,生成RF关键字及 自动化用例

    背景 因为服务的迁移,Jira版本的更新,很多接口文档的维护变少,导致想要编写部分服务的自动化测试变得尤为麻烦,很多服务,尤其是客户端接口需要通过抓包的方式查询参数来编写自动化用例,但是过程中手工重复 ...

  6. SDKStyle的Framework项目使用旧版项目文件生成的Nuget包遇到的问题

    随笔-2021-11-10 SDKStyle的Framework项目使用旧版项目文件生成的Nuget包遇到的问题 简介 C#从NetCore之后使用了新版的项目文件,SDK-Style项目,新版本的项 ...

  7. 【SSH项目实战三】脚本密钥的批量分发与执行

    [SSH项目实战]脚本密钥的批量分发与执行 标签(空格分隔): Linux服务搭建-陈思齐 ---本教学笔记是本人学习和工作生涯中的摘记整理而成,此为初稿(尚有诸多不完善之处),为原创作品,允许转载, ...

  8. Robot Framework测试框架用例脚本设计方法

    Robot Framework介绍 Robot Framework是一个通用的关键字驱动自动化测试框架.测试用例以HTML,纯文本或TSV(制表符分隔的一系列值)文件存储.通过测试库中实现的关键字驱动 ...

  9. Robot Framework 项目搭建

    首先新建一个项目“RobotDemo".项目Type一般选择“Directory”形式. 项目第一层可以放3种文件:Test Suite.Directory 和 Resource File. ...

随机推荐

  1. 常用shell命令积累

    把学习工作中见到的shell命令积累下来 创建文件夹 mkdir 创建文件 touch 发送get请求 curl xxxxx 发送post请求 curl -d xxxxx

  2. 货币转换B

    描述 人民币和美元是世界上通用的两种货币之一,写一个程序进行货币间币值转换,其中:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮ ...

  3. HashMap、HashTable差异详解

    HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的Has ...

  4. ASE —— 第一次结对作业

    问题定义 游戏规则: N个玩家,每人写一个或两个0~100之间的有理数 (不包括0或100),提交给服务器,服务器在当前回合结束时算出所有数字的平均值,然后乘以0.618,得到G值. 提交的数字最靠近 ...

  5. flume 1.7在windows下的安装部署与测试运行

    一.安装 安装java,配置环境变量. 安装flume,下载地址,下载后直接解压即可. 二.运行 创建配置文件:在解压后的文件 apache-flume-1.7.0-bin\conf下创建一个exam ...

  6. ImportError: No module named yaml

    问题: import yaml ImportError: No module named yaml 解决: wget http://pyyaml.org/download/pyyaml/PyYAML- ...

  7. Vs2017 NetCode Mvc EF Mysql 整合1

    1  运行环境   vs2017   NetCode2.0 2 NuGet  MySql.Data.EntityFrameworkCore 8.0.18 3  源代码 https://github.c ...

  8. linux配置全局环境变量-jdk

    1.vi /etc/profile 2.输入大写G,定位内容末尾. 3.在末尾输入 export JAVA_HOME=/home/order/soft/jdk PATH=$PATH:$HOME/.lo ...

  9. 获取当前日期或者某个日期相隔N天内的全部日期以及星期几

    业务需要需要获取当前日期相隔30天内的全部日期以及星期几,没插件因此特地写了一个: /* 说明:获取当前日期或者某个日期相隔N天内的全部日期以及星期几 使用: let test = new getdi ...

  10. Qtxlsx

    https://blog.csdn.net/qq_40194498/article/details/80817264