Python+Pytest+Allure+Git+Jenkins介面自動化框架
一、介面基礎
介面測試是對系統和元件之間的介面進行測試,主要是效驗資料的交換,傳遞和控制管理過程,以及相互邏輯依賴關係。其中介面協議分為HTTP,RPC,Webservice,Dubbo,RESTful等型別。
介面測試流程
1、需求評審,熟悉業務和需求
2、開發提供介面文件
3、編寫介面測試用例
4、用例評審
5、提測後開始測試
6、提交測試報告
兩種常見的 HTTP 請求方法:GET 和 POST
二、專案說明
本框架是一套基於Python+Pytest+Requests+Allure+Jenkins而設計的資料驅動介面自動化測試的框架。
技術棧
Python、Pytest、Requests、Pactverity、Excel、Json、Mysql、Allure、Logbook、Git、Jenkins
三、介面測試框架結構圖
四、專案功能
Python+Pytest+Allure+Jenkins介面自動化框架,實現Excel或Json維護測試用例,支援資料庫操作,利用封裝的請求基類調取相應的測試用例介面,獲取配置檔案中的環境地址與環境變數,
結合Pytest進行單元測試,使用LogBook進行記錄日誌,並生成allure測試報告,最後進行Jenkins整合專案實現整合部署,併傳送測試報告郵件。
五、程式碼設計與功能說明
1、工具類封裝
1.1、log日誌
專案中的log日誌是logbook進行日誌記錄的,方便測試開發除錯時進行排錯糾正或修復優化。日誌可選擇是否列印在螢幕上即執行時是否在終端輸出列印。日誌格式輸出可調整。
handle_log.py部分原始碼
1 def log_type(record, handler): 2 log = "[{date}] [{level}] [{filename}] [{func_name}] [{lineno}] {msg}".format( 3 date=record.time, # 日誌時間 4 level=record.level_name, # 日誌等級 5 filename=os.path.split(record.filename)[-1], # 檔名 6 func_name=record.func_name, # 函式名 7 lineno=record.lineno, # 行號 8 msg=record.message # 日誌內容 9 ) 10 return log 11 # 日誌存放路徑 12 LOG_DIR = BasePath + '/log' 13 print(LOG_DIR) 14 if not os.path.exists(LOG_DIR): 15 os.makedirs(LOG_DIR) 16 # 日誌列印到螢幕 17 log_std = ColorizedStderrHandler(bubble=True) 18 log_std.formatter = log_type 19 # 日誌列印到檔案 20 log_file = TimedRotatingFileHandler( 21 os.path.join(LOG_DIR, '%s.log' % 'log'), date_format='%Y-%m-%d', bubble=True, encoding='utf-8') 22 log_file.formatter = log_type 23 24 # 指令碼日誌 25 run_log = Logger("global_log") 26 def init_logger(): 27 logbook.set_datetime_format("local") 28 run_log.handlers = [] 29 run_log.handlers.append(log_file) 30 run_log.handlers.append(log_std) 31 return ""
列印在終端的日誌,如下圖所示。
同時執行專案後,會在專案檔案log中自動生成一個以當天日期命名的log檔案。點選log日誌檔案可檢視日誌詳情即專案執行時所記錄的日誌或報錯日誌。如下圖所示。
1.2、配置檔案
專案中涉及到一些配置檔案如username、password或環境變數時,我們可通過配置檔案來獲取配置值。通過配置檔案中key與value的定義來確定獲取配置檔案的值。
handle_init.py部分原始碼
1 class HandleInit: 2 # 讀取配置檔案 3 def load_ini(self): 4 file_path = BasePath + "/config/config.ini" 5 cf = configparser.ConfigParser() 6 cf.read(file_path, encoding='UTF-8') 7 return cf 8 9 # 獲取ini裡面對應key的value 10 def get_value(self, key, node=None): 11 if node == None: 12 node = 'Test' 13 cf = self.load_ini() 14 try: 15 data = cf.get(node, key) 16 logger.info('獲取配置檔案的值,node:{},key:{}, data:{}'.format(node, key, data)) 17 except Exception: 18 logger.exception('沒有獲取到對應的值,node:{},key:{}'.format(node, key)) 19 data = None 20 return data
獲取配置檔案中的值日誌如下圖所示。
1.3、Api介面請求
獲取相關測試用例及介面用例配置,記錄請求相關引數的日誌,定義Allure測試報告的步驟。
handle_apirequest.py部分程式碼
1 class ApiRequest: 2 def api_request(self, base_url, test_case_data, case_data): 3 get_name = None 4 get_url = None 5 get_method = None 6 get_headers = None 7 get_cookies = None 8 get_case_name = None 9 get_case_params = None 10 response_data = None 11 try: 12 get_name = test_case_data['config']['name'] 13 get_url = base_url + test_case_data['config']['url'] 14 get_method = test_case_data['config']['method'] 15 get_headers = test_case_data['config']['headers'] 16 get_cookies = test_case_data['config']['cookies'] 17 except Exception as e: 18 logger.exception('獲取用例基本資訊失敗,{}'.format(e)) 19 try: 20 get_case_name = case_data['name'] 21 get_case_params = case_data['params'] 22 except Exception as e: 23 logger.exception('獲取測試用例資訊失敗,{}'.format(e)) 24 with allure.step("請求介面:%s,請求地址:%s,請求方法:%s,請求頭:%s,請求Cookies:%s" % ( 25 get_name, get_url, get_method, get_headers, get_cookies)): 26 allure.attach("介面用例描述:", "{0}".format(get_case_name)) 27 allure.attach("介面用例請求引數:", "{0}".format(get_case_params)) 28 logger.info( 29 '請求介面名:%r,請求地址:%r,請求方法:%r,請求頭:%r,請求Cookies:%r' % (get_name, get_url, get_method, get_headers, get_cookies)) 30 logger.info('請求介面名:%r,請求介面用例名:%r,介面用例請求引數:%r' % (get_name, get_case_name, get_case_params)) 31 try: 32 response_data = baseRequest.run_main(get_method, get_url, get_case_params, get_headers) 33 except Exception as e: 34 logger.exception('用例請求返回失敗,{}'.format(e)) 35 logger.info('請求介面名:%r,請求介面用例名:%r,返回引數:%r' % (get_name, get_case_name, response_data.json())) 36 return response_data
1.4、Excel資料處理
1.4.1、Excel測試用例
測試用例中維護在Excel檔案中,類中定義如何獲取Excel中的相關資料(如獲取某個單元格的內容,獲取單元格的行數,以及將資料寫入Excel中等操作)。
handle_exceldata.py部分原始碼
1 class OperationExcel: 2 def __init__(self, file_name=None, sheet_id=None): 3 if file_name: 4 self.file_name = file_name 5 self.sheet_id = sheet_id 6 else: 7 self.file_name = '' 8 self.sheet_id = 0 9 self.data = self.get_data() 10 11 # 獲取sheets的內容 12 def get_data(self): 13 data = xlrd.open_workbook(self.file_name) 14 tables = data.sheets()[self.sheet_id] 15 return tables 16 17 # 獲取單元格的行數 18 def get_lines(self): 19 tables = self.data 20 return tables.nrows 21 22 # 獲取某一個單元格的內容 23 def get_cell_value(self, row, col): 24 return self.data.cell_value(row, col)
1.5、Json資料處理
1.5.1、Json測試用例
1 { 2 "config":{ 3 "name":"post介面名", 4 "url":"/langdetect", 5 "method":"POST", 6 "headers":{ 7 "Content-Type":"application/json" 8 }, 9 "cookies":{ 10 11 } 12 }, 13 "testcase":[ 14 { 15 "name":"測試用例1", 16 "params":{ 17 "query":"測試" 18 }, 19 "validate":[ 20 { 21 "check":"status_code", 22 "comparator":"eq", 23 "expect":"200" 24 } 25 ] 26 }, 27 { 28 "name":"測試用例2", 29 "params":{ 30 "query":"python" 31 }, 32 "validate":[ 33 { 34 "check":"msg", 35 "comparator":"eq", 36 "expect":"success" 37 } 38 ] 39 } 40 ] 41 }
1.5.2、Json用例處理
獲取Json檔案中裡具體欄位的值。
handle.json.py部分原始碼
1 class HandleJson: 2 # 讀取json檔案 3 def load_json(self, file_name): 4 if file_name == None: 5 file_path = "" 6 else: 7 file_path = file_name 8 try: 9 with open(file_path, encoding='UTF-8') as f: 10 data = json.load(f) 11 return data 12 except Exception: 13 print("未找到json檔案") 14 return {} 15 16 # 讀取json檔案裡具體的欄位值 17 def getJson_value(self, key, file_name): 18 if file_name == None: 19 return "" 20 jsonData = self.load_json(file_name) 21 if key == None: 22 getJsonValue = "" 23 else: 24 getJsonValue = jsonData.get(key) 25 return getJsonValue
2、基類封裝
2.1、請求基類封裝
介面支援Get、Post請求,呼叫requests請求來實現介面的呼叫與返回。介面引數包括,介面地址、介面請求引數、cookie引數、header引數。
1 class BaseRequest: 2 3 def send_get(self, url, data, header=None, cookie=None): 4 """ 5 Requests傳送Get請求 6 :param url:請求地址 7 :param data:Get請求引數 8 :param cookie:cookie引數 9 :param header:header引數 10 """ 11 response = requests.get(url=url, params=data, cookies=cookie, headers=header) 12 return response 13 14 def send_post(self, url, data, header=None, cookie=None): 15 """ 16 Requests傳送Post請求 17 :param url:請求地址 18 :param data:Post請求引數 19 :param data:Post請求引數 20 :param cookie:cookie引數 21 :param header:header引數 22 """ 23 response = requests.post(url=url, json=data, cookies=cookie, headers=header) 24 return response 25 26 # 主函式呼叫 27 28 def run_main(self, method, url, data, header, cookie=None): 29 try: 30 result = '' 31 if method.upper() == 'GET': 32 result = self.send_get(url, data, header, cookie) 33 elif method.upper() == 'POST': 34 result = self.send_post(url, data, header, cookie) 35 return result 36 except Exception as e: 37 logger.exception('請求主函式呼叫失敗:{}'.format(e))
3、介面測試用例編寫
3.1、介面測試用例
引用Pytest來進行介面的單元測試,通過JSON中多個測試用例來做為引數化資料驅動。結合Allure制定相應介面的測試報告。在介面返回斷言之前,我們先進行該介面的契約測試,
我們採用的是Pactverity的全量契約校驗測試。當契約測試通過時,我們再進行返回引數的相關校驗測試。
test_getRequestJson.py部分原始碼
1 @allure.feature('測試GET請求模組') 2 class TestRequestOne(): 3 @allure.title('測試標題') 4 @allure.testcase('測試地址:https://www.imooc.com') 5 @pytest.mark.parametrize('case_data', testCaseData['testcase']) 6 def test_requestOne(self, case_data): 7 try: 8 api_response = apiRequest.api_request(baseurl, testCaseData, case_data) 9 api_response_data = api_response.json() 10 # pactverity——全量契約校驗 11 config_contract_format = Like({ 12 "msg": "成功", 13 "result": 0, 14 "data": EachLike({ 15 "word": Like("testng") 16 }) 17 }) 18 mPactVerify = PactVerify(config_contract_format) 19 try: 20 mPactVerify.verify(api_response_data) 21 logger.info( 22 'verify_result:{},verify_info:{}'.format(mPactVerify.verify_result, mPactVerify.verify_info)) 23 assert mPactVerify.verify_result == True 24 except Exception: 25 err_msg = '契約校驗錯誤' 26 logger.exception('測試用例契約校驗失敗,verify_result:{},verify_info:{}'.format(mPactVerify.verify_result, 27 mPactVerify.verify_info)) 28 try: 29 for case_validate in case_data['validate']: 30 logger.info('斷言期望相關引數:check:{},comparator:{},expect:{}'.format(case_validate['check'], 31 case_validate['comparator'], 32 case_validate['expect'])) 33 comparatorsTest.comparators_Assert(api_response, case_validate['check'], 34 case_validate['comparator'], case_validate['expect']) 35 logger.info('測試用例斷言成功') 36 except Exception as e: 37 logger.exception('測試用例斷言失敗') 38 except Exception as e: 39 logger.exception('測試用例請求失敗,原因:{}'.format(e))
3.2、主執行
運用Pytest和Allure的特性,命令列執行測試用例資料夾,並生成對應的allure測試報告。
1 if __name__ == "__main__": 2 pytest.main(['-s', '-v', 'test_case/testRequest/', '-q', '--alluredir', 'reports'])
4、Allure2測試報告
當我們執行主函式時,並生成對應的測試用例報告時,我們可以看到在該資料夾中會生成對應的json檔案的測試報告。將json檔案的測試報告轉換成html形式的。命令如下
reports是json格式測試報告存放的目錄位置,allure_reports是html測試報告檔案生成的目錄位置。allure命令如下。
1 allure generate reports -o allure_result/
專案根目錄下的allure_reports檔案,存放的是allure生成的測試報告。可看出檔案下有一個HTML檔案,可通過Python的編輯器Pycharm來開啟該HTML檔案(測試報告),
或可通過allure命令來開啟該HTML,展示HTML測試報告。如下所示。
測試報告檔案,HTML測試報告如下。
allure命令開啟HTML測試報告。命令如下所示。
1 allure open allure_result/
如下圖所示。
開啟生成的HTML測試報告如下圖所示。
5、Jenkins整合
Allure+Jenkins的分享,我之前在Pytest+Allure+Jenkins的部落格中已經分享過了。這塊可以出門左轉看看。前期的準備就不在這裡重複說明了。我們就直接來上手建立Item進行相關配置。
General中GitHub專案地址的配置,將自己專案的Git複製至專案URL處。如下圖所示。
原始碼管理設定。勾選Git,填寫相應的專案Git地址,Git專案許可權所有者,以及對應的拉取程式碼的分支。如下圖所示。
配置構建命令。選擇“執行windows批處理命令”,用python執行主函式執行指令碼,命令如下圖所示。
當我們在Jenkins裡面成功安裝Allure外掛後,直接可以在構建後操作中配置Allure的相關配置。在Pytest+Allure+Jenkins中已經說明過的。
Results應與專案執行時設定的Allure生成的Json格式報告的路徑一致,Report path為Allure html報告結果生成檔案存放的路徑。
排除萬難之後,我們就可以用Jenkins來執行專案了。如下圖所示。
測試報告詳情頁,如下圖所示。
六、後期優化
1、介面測試用例之間的資料依賴
2、測試報告郵件的傳送
。。。。。。
七、感想
該框架是在涉及python的知識點比較多,將介面測試與契約測試結合起來。該框架是在工作之餘學習多篇文章,實戰上手逐步入門開始的,適合新手入門介面自動化實戰練習,僅供參考學習。框架中有不少可優化點與不足點,希望大家多多提建議或想法。
開源地址:https://github.com/wuwei88/Apiautomation.git