Python+Pytest+Allure+Git+Jenkins介面自動化框架

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('測試地址://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的知識點比較多,將介面測試與契約測試結合起來。該框架是在工作之餘學習多篇文章,實戰上手逐步入門開始的,適合新手入門介面自動化實戰練習,僅供參考學習。框架中有不少可優化點與不足點,希望大家多多提建議或想法。

  開源地址://github.com/wuwei88/Apiautomation.git