Time will tell.

1、前言
今年2月份調去支持專案介面測驗,測驗程序中使用過postman、jmeter工具,基本能滿足使用,但是部分情況下使用較為麻煩,
比如:部分欄位存在唯一性校驗或欄位間有業務性校驗,每次請求均需手工修改部分報文內容,使用工具難以滿足實際使用,
因此,萌生了使用python去實作介面自動化的想法,之前未接觸過介面測驗,但有一點編程基礎,經過2個多月的磕磕碰碰,不斷完善,經歷2次重構之后,最后基本達成了目標,
2、技術堆疊
1、python語言
2、requests庫
3、unittest單元測驗框架
4、HTMLTestReportCN、BeautifulReport測驗報告
3、實作的功能概述
1、支持post、get等請求型別,xml、json格式的報文
2、支持使用excel撰寫測驗用例,測驗用例支持涉及多介面的場景用例;支持按腳本的形式撰寫測驗用例
3、支持測驗結果保存至資料庫,支持生成html報告,支持將生成的測驗結果匯出到excel檔案
4、支持郵件發送測驗結果
5、支持多執行緒并發執行測驗用例
4、框架及專案結構
APIS_AutoTest
api: 主程式目錄
comm:公共函式,包括:介面請求基類、請求及相應資料操作基類等
intf_handle:介面操作層,包含:介面初始化、斷言等
business:業務實作部分
utils:工具類,包括:讀取檔案、發送郵件、excel操作、資料庫操作、日期時間格式化等
config:組態檔目錄,包含yaml組態檔、以及路徑配置
data:測驗資料目錄,用于存放測驗資料
temp:臨時檔案目錄,用于存放臨時檔案
result:結果目錄
report:測驗報告目錄,用于存放生成的html報告
details:測驗結果詳情目錄,用于存放生成的測驗用例執行結果excel檔案
log:日志檔案目錄
test:測驗用例、測驗集相關目錄,啟動test_suite執行用例檔案存放在此
test_case:測驗用例存放路徑
test_suite:測驗模塊集,按模塊組裝用例
5、測驗用例執行流程
以腳本形式撰寫的測驗用例執行流程圖:
以excel形式撰寫的測驗用例執行流程圖:
6、核心方法設計
介面請求基類
RequestBase(request_type, url, header, body, data_type=None)
功能描述:
根據傳入的請求型別,請求地址,請求頭,請求體,發送介面請求,獲得回應頭,回應體,
-
request_type: 請求型別,只能是’GET’, ‘POST’, ‘HEAD’, ‘OPTIONS’, ‘PUT’,
‘DELETE’, ‘TRACE’, 'CONNECT’中的一個,無大小寫要求; -
url: 請求地址,完整的介面地址;
-
head: 請求頭;
-
body:請求體,xml格式請求體為字串; json格式請求體需傳入json格式;
-
data_type: 資料型別,未使用欄位,備用,可為:xml、json,
支持方法
__send_request()
發送請求,類私有方法,初始化時呼叫,呼叫方法回傳:回應物件
get_respond()
獲取回應物件,呼叫方法回傳:回應物件
get_respond_head()
獲取回應頭,呼叫方法回傳:回應頭
get_respond_body()
獲取回應體,呼叫方法回傳:回應體
請求或回應資料操作基類
RequestRespondHandle(data_type, body, fields, value_dict=None)
功能描述:
根據傳入的xml、json格式請求體或回應體,讀取欄位的值或更新欄位的值
引數描述:
-
data_type: 資料型別,當前僅支持xml、json,不區分大小寫
-
body: 請求體或回應體,json格式請求體支持傳入字典、json格式資料(自動轉換為字典)
-
fields: 欄位名稱,支持多欄位傳入,支持資料型別:字串、元組、串列,多欄位傳入形式,字串:多個欄位名稱之間逗號隔開,如:‘a1,b2’;元組、串列正常傳入即可,json格式:需寫入完整節點路徑,如:body.base.name,對應list型別的需傳入索引位置,如:body.baselist[0].name
-
value_dict: 欄位值字典,以欄位名稱及欄位值鍵值對的方式存盤資料,讀取欄位值時,一般不需傳入,也支持傳入(用于讀取excel形式流程前后傳值);更新欄位值時,需傳入,且字典中的key值需要與fields中的欄位名對應,
支持方法:
get_fields_value()
獲取欄位值,呼叫方法即可獲取到xml或json格式的請求體或回應體對應的欄位值,并回傳:欄位值字典
update_fields_value()
更新欄位值,呼叫方法即可更新請求體中對應的欄位值,回傳更新后的請求體,
Json格式請求體回傳格式為字典,
不足與改進:
xml格式,讀取或更新欄位時,若存在多個相同名稱欄位,默認只選第一個;
json格式,讀取嵌套串列的時候,未支持按串列讀取,當前需精確位置單個讀取或更新;
請求體初始化-介面映射類
RequestMsgInitMapper(data_type, intf_code, request_body, **kwargs)
功能描述:
根據傳入的介面編號,映射到對應的介面請求報文的初始化方法,進行介面初始化
引數描述:
-
data_type: 資料型別,執行xml、json,不區分大小寫
-
intf_code: 介面編號,每個介面都要一個介面編號,根據介面編號可以唯一確定一個介面
request_body: 請求體,json格式請求體支持傳入字典、json格式資料 -
kwargs: 可變關鍵字引數,每個介面初始化時,引數數量均不一致,所有需要使用可變引數,需要以a=value這種方式傳入,部分欄位(如id)可在方法內設定生成方法,一般可變引數設定都是用于前后介面欄位傳值,
支持方法:
start_Intf_init_mapper()
啟動 介面初始化映射,該方法通過判斷傳入資料型別、介面編號,執行對應的介面初始化方法,
呼叫方法后,進行對應介面的初始化,并回傳初始化后的請求體,
Json格式的資料,回傳json格式的請求體(不管傳入的是字典格式、還是json格式),
適用場景說明:
介面映射類主要是針對不同介面初始化欄位涉及復雜業務判斷,需按介面單獨撰寫的場景,
如果一個介面初始化涉及的欄位均不涉及業務相關的復雜判斷處理,可以直接統一使用通用介面初始化方法進行初始化,
通用介面初始化方法
intf_base_init(data_type, request_body, **kwargs)
功能描述:
根據可變關鍵字引數傳入的鍵值對,進行介面報文初始化,回傳初始化后的請求體
引數描述:
-
data_type: 資料型別,執行xml、json,不區分大小寫
-
request_body: 請求體,json格式請求體支持傳入字典、json格式資料
-
kwargs:可變關鍵字引數,每個介面初始化時,引數數量均不一致,所有需要進行初始化的欄位,均需以鍵值對的方式傳入,
適用場景說明:
介面初始化欄位不涉及復雜邏輯判斷,直接傳值后更新即可;該方式也適用于介面請求頭的初始化,
7、資料操作類
資料操作主要是資料讀取、寫入,主要分為如下幾類:
- 讀取txt、json等檔案內容(整個讀取)、寫入內容到檔案
- 讀取yaml檔案內容
- 讀取excel表格內容、匯出excel表格、讀取excel并作為模板
- 操作資料庫表中的資料(增刪改查)
讀取txt/json檔案內容,寫入內容到檔案,
FileHandle(file_name, file_path)
功能描述:
讀取檔案所有內容
引數描述:
file_name:讀取/寫入檔案名稱,包含后綴,支持txt,json等;
file_path:讀取/寫入檔案所在的目錄,
支持方法:
read_file_content()
讀取檔案內容,并以字串回傳
write_to_file(content)
將內容寫入檔案
讀取yaml檔案內容
ReadYaml(file_path)
功能描述:
讀取yaml檔案內,支持按名稱讀取
引數描述:
file_path:讀取檔案的完整路徑
支持方法:
get_yaml()
讀取yaml檔案所有內容,并以字典格式回傳
get_value(level_name)
讀取yaml檔案欄位的值,并回傳
level_name: 節點欄位名稱,如涉及多節點需傳入對應路徑,如:db.host
讀取excel表格內容、匯出excel表格、讀取excel并作為模板
ExcelHandle()
功能描述:
讀取excel表格內容、將資料匯出到excle表格中
支持方法:
read_excel_data(excel_path, sheet_name=None)
讀取excel表格內容,并以串列嵌套串列的方式回傳
excel_path: 讀取excel檔案的完整路徑
sheet_name: 讀取excel的頁簽名稱,默認讀取第一個頁簽
export_to_excel(data, head, file_name, time_flag=None)
將資料匯出到excle表格中
data: 需要匯出到excel的內容,元組嵌套元組(對應資料庫中查詢回傳的結果)
head: excel表頭,串列、元組嵌套元組(資料庫中查詢表頭、描述,支持多表頭)
file_name: excel檔案名稱,包含后綴.xlsx,excel匯出路徑系統默認
time_flag: 時間戳標記,可傳入用例執行的報告號,使其對應
copy_excel_template(template_path, sheet_name=None)
復制excel表格模板,回傳workbook, workssheet
template_path: 模板檔案完整路徑
sheet_name: 模板檔案頁簽名稱,默認第一個頁簽
操作資料庫資料
功能描述:
表資料讀取:根據情況讀取所需資料,回傳資料格式元組嵌套元組,
插入/更新表資料:根據實際內容,寫入資料到對應的表中,
備注:
資料庫部分封裝為資料庫操作類,
8、部分關鍵代碼
介面請求基類
# create by: wyun # create at: 2020/4/19 9:30 import json import requests class RequestBase: def __init__(self, request_type, request_url, request_body, request_headers, intf_type=None): """ 請求介面公共類 :param request_type: 請求型別:post, get :param request_url: 請求url地址 :param request_headers: 請求頭 :param request_body: 請求體(xml型別傳入字串格式,json型別資料必須傳入json格式,不能傳入字典) :param intf_type: 介面型別:webservice, api,分別對應介面資料格式:xml, json """ self.request_type = request_type self.request_url = request_url self.request_body = request_body self.request_headers = request_headers self.intf_type = intf_type self.res = self.__send_request() # 發送請求 def __send_request(self): if not isinstance(self.request_type, str): print('請求型別格式錯誤,') return None if self.request_type.upper() not in ['GET', 'POST', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'CONNECT']: print('請求型別不存在,') return None return requests.request(method=self.request_type, url=self.request_url, data=https://www.cnblogs.com/flowToken1024532/archive/2020/09/29/self.request_body.encode('utf-8'), headers=self.request_headers) # 獲取回應 def get_respond(self): return self.res # 獲取回應頭 def get_respond_head(self): return self.res.headers # 獲取回應體 def get_respond_body(self): if self.res.content: return self.res.text
請求或回應資料操作基類
# create by: wyun # create at: 2020/4/19 12:58 import json import re """ 對請求體或回應體進行處理: 1. 支持讀取請求體或回應體中欄位的值 2. 支持更新請求體中欄位的值 """ # 遞回呼叫,更新json中的欄位值 def update_json_step(node, json_str, value, i=0): # 當前節點索引(負向) node_index = -len(node) + i # 如果包含[n]形式,說明節點為串列,需處理 if '[' in node[node_index] and ']' in node[node_index]: list_node, list_index = node[node_index].split('[') index = list_index.split(']')[0] if index is None or index == '': print('引數傳入錯誤,請指定串列[%s]索引' % list_node) else: index = int(index) return update_json_step(node, json_str[list_node][index], value, i + 1) # 判斷如果當前節點為最后一個節點,則更新value值 if node_index == -1: json_str[node[-1]] = value return json_str return update_json_step(node, json_str[node[node_index]], value, i + 1)
class DataHandle: def __init__(self, data_type, data_msg, fields, value_dict=None): """ 處理請求體或回應體資料,讀取或更新欄位值 :param data_type: 資料型別:xml,json :param data_msg: 請求體或回應體 :param fields: 欄位值,支持多字讀方式,欄位間逗號隔開;或傳入串列、元組 json格式:需寫入完整節點路徑,如:body.base.name,對應list型別的需傳入索引位置,如:body.baselist[0].name :param value_dict: 以字典鍵值對保存欄位值 """ self.data_type = data_type self.data = data_msg # fields支持str,list方式,str自動轉換為list if isinstance(fields, str): self.fields = fields self.fields_list = [] if ',' in fields: self.fields_list = fields.strip().split(',') else: self.fields_list.append(fields.strip()) elif isinstance(fields, list): self.fields_list = fields else: self.fields_list = list(fields) # 初始化字典值 if value_dict is None: self.value_dict = dict() else: self.value_dict = value_dict def get_fields_value(self): """ 獲取欄位值 1. 支持獲取多個欄位值,輸入字串或串列, 元組 """ step_dict = self.value_dict # 欄位串列回圈獲取 for p in self.fields_list: if p == '': continue # 處理xml格式報文 if self.data_type.upper() == 'XML': pattern = '<' + p + '>.*</' + p + '>' search_result = re.search(pattern, self.data) if search_result is not None: # 包括標簽和值都匹配上 field_and_value =https://www.cnblogs.com/flowToken1024532/archive/2020/09/29/ search_result.group() # 去除標簽獲取欄位值,并存入字典 step_dict[p] = (field_and_value.split('</')[0]).split('>')[-1] else: # print('引數[%s]提取失敗,無匹配值,' % p) pass # 處理json格式報文 elif self.data_type.upper() == 'JSON': # 獲取層級(使用.隔開) node = p.split('.') # json轉換字典 if isinstance(self.data, dict): temp = self.data else: temp = json.loads(self.data) # 逐層讀取資料 for per_node in node: if '[' in per_node and ']' in per_node: per_node, index_str = per_node.split('[') index = int(index_str.split(']')[0]) temp = temp.get(per_node)[index] if temp is None: break else: temp = temp.get(per_node) if temp is None: break # 講讀取的結果寫入字典 step_dict[p] = temp # 非 xml,json的資料格式,報錯退出 else: print('ERROR: 不支持此資料型別[%s]' % self.data_type) break return step_dict def update_fields_value(self): """ 更新欄位值 支持更新單個欄位值,多個欄位值(欄位名 字串或串列、元組,欄位值 字典) """ req_body = self.data # 欄位串列回圈更新 for p in self.fields_list: # 入參型別判斷 if isinstance(self.value_dict, dict): # 引數在字典中不存在,則跳過 if p not in self.value_dict.keys(): print('ERROR: 字典中不存在引數[%s],' % p) continue # 獲取字典中引數的值 value =https://www.cnblogs.com/flowToken1024532/archive/2020/09/29/ self.value_dict[p] elif isinstance(self.value_dict, str): value = self.value_dict else: print('函式入參[%s]型別不支持,請傳入字串或字典,' % self.value_dict) break # 更新資料型別為 xml 的引數值 if self.data_type.upper() == 'XML': # 優先尋找是否存在指定更新引數,若有,則按指定引數更新 if '${' + p + '}' in req_body: req_body = req_body.replace('${' + p + '}', value) # 其次尋找標簽引數值,若有,則按標簽更新值 else: # 檢查標簽是否存在,若存在 if '<' + p + '>' in req_body and '</' + p + '>' in req_body: # 正則匹配 pattern = '<' + p + '>.*</' + p + '>' new_field_and_value = '<' + p + '>' + str(value) + '</' + p + '>' search_result = re.search(pattern, req_body) # 若正則匹配結果存在,則進行欄位值更新 if search_result is not None: old_field_and_value = search_result.group() req_body = req_body.replace(old_field_and_value, new_field_and_value) else: print('引數[%s]匹配標簽失敗,請檢查報文中標簽格式,' % p) else: # print('引數[%s]匹配標簽失敗,無此標簽,' % p) continue # 更新資料型別為 xml 的引數值 elif self.data_type.upper() == 'JSON': # json轉換字典 if isinstance(self.data, dict): req_body = self.data else: req_body = json.loads(self.data) # 獲取層級 node = p.split('.') # 呼叫遞回函式更新欄位值 update_json_step(node, req_body, value) # 非 xml,json的資料格式,報錯退出 else: print('ERROR: 不支持此資料型別[%s]' % self.data_type) break return req_body
介面初始化通用類
# create by: wyun # create at: 2020/4/23 22:42 from api.comm.data_handle import DataHandle def intf_base_init(data_type, request_body, **kwargs): """ 介面初始化通用類 :param data_type: 資料型別,如:xml,json :param request_body: 請求體 :param kwargs: 可變關鍵字引數 :return: 請求體 """ return DataHandle(data_type, request_body, kwargs.keys(), kwargs).update_fields_value()

絮叨
如果你處于想學Python自動化或正在學習Python自動化中,Python自動化的教程不少了,但不一定是最新的,說不定你學的是別人一年前就學過的內容,干貨分享一波!2020最新的Python教程,獲取方式,加175317069私信Q群管理即可免費獲取,
喜歡的話,歡迎【評論】、【點贊】、【關注】禮貌三連
Time will tell.(時間會證明一切)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/140215.html
標籤:其他
