Python3-介面自動化-3-介面自動化專案目錄框架
阿新 • • 發佈:2020-10-19
一、專案結構
1. 新建一個工程,工程名為:sales_interface_auto
2. 在工程的根目錄新建一個py指令碼:runAll.py 執行介面自動化的入口,專案工程部署完畢後直接執行該檔案即可
3. 在專案下建立幾個package包:
----common:這個包放置一些公共的方法,例如:讀取excel,讀取mysql,get和post請求的封裝,傳送Email的封裝,讀取手機公共引數的封裝,Log.py是封裝日誌的輸入
----config:這個包裡是放置一些獲取根資料夾目錄,介面伺服器地址,讀寫配置檔案封裝的方法
----result: 該包裡存放日誌,截圖,HTML報告
----testCase:這個包放test開頭的測試用例,也可以放一些封裝介面的方法
----testFile/case:存放excel測試用例
----testModels :存放對應介面指令碼的裝飾器提取器
----util/generator:介面測試中用到的一些資料的生成,如:身份證號,名字,手機號,隨機字串等
----caselist.txt :順序存放testCase中test打頭的用例,切記注意順序,無需執行時,首位加#號即可
----config.ini :這裡是配置檔案,如郵箱的一些引數,資料庫,手機靜態引數,以及存放測試過程生成的引數
----config_url.ini:這裡是配置檔案,存放介面地址
二、詳細介紹個目錄
1.runAll.py
原始碼如下:
import os import common.HTMLTestRunner as HTMLTestRunner from config import readConfig, getpathInfo import unittest from common.configEmail import send_email import common.Log from testCase.test_init_user_info import InitUserInfo from config import readConfig as readConfig, writeConfig as writeConfig, readExcel, geturlParams writeconfig = writeConfig.WriteConfig() send_mail = send_email() path = getpathInfo.get_Path() report_path = os.path.join(path, 'result') on_off = readConfig.ReadConfig().get_email('on_off') loggger = common.Log.logger #定義一個類AllTest class AllTest: # 初始化一些引數和資料 def __init__(self): global resultPath # result/report.html resultPath = os.path.join(report_path, "report.html") # 配置執行哪些測試檔案的配置檔案路徑 self.caseListFile = os.path.join(path, "caselist.txt") # 真正的測試斷言檔案路徑 self.caseFile = os.path.join(path, "testCase") # 初始化一下測試中用到的資料,並存儲到配置檔案 self.idno = InitUserInfo().generate_id_no() self.c_name = InitUserInfo().generate_c_name() self.c_mobile = InitUserInfo().generate_mobile() self.caseList = [] writeconfig.write_potentiall_user_info(self.idno,self.c_name,self.c_mobile) loggger.info('resultPath'+resultPath)# 將resultPath的值輸入到日誌,方便定位檢視問題 loggger.info('caseListFile'+self.caseListFile) loggger.info('caseList'+str(self.caseList)) def set_case_list(self): """ 讀取caselist.txt檔案中的用例名稱,並新增到caselist元素組 :return: """ fb = open(self.caseListFile) for value in fb.readlines(): data = str(value) if data != '' and not data.startswith("#"):# 如果data非空且不以#開頭 self.caseList.append(data.replace("\n", ""))# 讀取每行資料會將換行轉換為\n,去掉每行資料中的\n fb.close() def set_case_suite(self): """ :return: """ self.set_case_list()# 通過set_case_list()拿到caselist元素組 test_suite = unittest.TestSuite() suite_module = [] for case in self.caseList:# 從caselist元素組中迴圈取出case case_name = case.split("/")[-1]# 通過split函式來將aaa/bbb分割字串,-1取後面,0取前面 print(case_name+".py")# 打印出取出來的名稱 # 批量載入用例,第一個引數為用例存放路徑,第一個引數為路徑檔名 discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None) suite_module.append(discover)# 將discover存入suite_module元素組 print('suite_module:'+str(suite_module)) if len(suite_module) > 0:# 判斷suite_module元素組是否存在元素 for suite in suite_module:# 如果存在,迴圈取出元素組內容,命名為suite for test_name in suite:# 從discover中取出test_name,使用addTest新增到測試集 test_suite.addTest(test_name) else: print('測試套件中無可用的測試用例') return None return test_suite def run(self): """ run test :return: """ try: suit = self.set_case_suite()#呼叫set_case_suite獲取test_suite print('try') print(str(suit)) if suit is not None:#判斷test_suite是否為空 print('if-suit') fp = open(resultPath, 'wb')# 開啟result/20181108/report.html測試報告檔案,如果不存在就建立 # 呼叫HTMLTestRunner runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description') runner.run(suit) else: print("Have no case to test.") except Exception as ex: print(str(ex)) # log.info(str(ex)) finally: print("*********TEST END*********") # log.info("*********TEST END*********") fp.close() # 判斷郵件傳送的開關 if on_off == 'on': send_mail.outlook() else: print("郵件傳送開關配置關閉,請開啟開關後可正常自動傳送測試報告") # pythoncom.CoInitialize() # scheduler = BlockingScheduler() # scheduler.add_job(AllTest().run, 'cron', day_of_week='1-5', hour=14, minute=59) # scheduler.start() if __name__ == '__main__': AllTest().run()
1.1__init__ 初始化資料
# 定義全域性變數 global resultPath # 取到report.html絕對路徑 resultPath = os.path.join(report_path, "report.html") # 拿到caselist.txt的絕對路徑
self.caseListFile = os.path.join(path, "caselist.txt") # 拿到testCase的絕對路徑 self.caseFile = os.path.join(path, "testCase") # 初始化一下測試中用到的資料,並存儲到配置檔案 self.idno = InitUserInfo().generate_id_no() self.c_name = InitUserInfo().generate_c_name() self.c_mobile = InitUserInfo().generate_mobile() self.caseList = [] writeconfig.write_potentiall_user_info(self.idno,self.c_name,self.c_mobile)
# 將resultPath等的值輸入到日誌,方便定位檢視問題
loggger.info('resultPath'+resultPath) loggger.info('caseListFile'+self.caseListFile) loggger.info('caseList'+str(self.caseList)) 1.2 set_case_list :讀取caselist.txt檔案中的用例名稱,並新增到caselist元素組
read() #一次性讀取文字中全部的內容,以字串的形式返回結果 readline() #只讀取文字第一行的內容,以字串的形式返回結果 readlines() #讀取文字所有內容,並且以數列的格式返回結果,一般配合for in使用
# 開啟caselist.txt檔案, fb = open(self.caseListFile) for value in fb.readlines(): data = str(value)
# 如果data非空且不以#開頭
if data != '' and not data.startswith("#"):
# 讀取每行資料會將換行轉換為\n,去掉每行資料中的\n
self.caseList.append(data.replace("\n", "")) fb.close() 1.3 set_case_suite 設定測試套件
# 通過set_case_list()拿到caselist元素組 self.set_case_list() test_suite = unittest.TestSuite() suite_module = [] # 從caselist元素組中迴圈取出case for case in self.caseList: case_name = case # 打印出取出來的名稱 print(case_name+".py") # 批量載入用例,第一個引數self.caseFile為用例存放路徑,第二個引數case_name為路徑檔名
discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None) # 將discover存入suite_module元素組 suite_module.append(discover) 輸出為此模式:找到測試用例檔案中的test開頭的方法
suite_module:[<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<paramunittest.TestLogin_0 testMethod=test01case>]>]>]>]
print('suite_module:'+str(suite_module)) # 判斷suite_module元素組是否存在元素 if len(suite_module) > 0: # 如果存在,迴圈取出元素組內容,命名為suite for suite in suite_module: # 從discover中取出test_name,使用addTest新增到測試集 for test_name in suite: test_suite.addTest(test_name) else: print('測試套件中無可執行的測試用例')
return None return test_suite 1.4 run 執行測試用例套件
try: # 呼叫set_case_suite獲取test_suite suit = self.set_case_suite() # print('try') # print(str(suit)) # 判斷test_suite是否為空 if suit is not None: # print('if-suit') # 開啟result/report.html測試報告檔案,如果不存在就建立 fp = open(resultPath, 'wb') # 呼叫HTMLTestRunner runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='UU隊長 測試報告', description='測試用例執行結果') # 通過HTMLTestRunner的run()方法來執行測試套件中的測試用例,並寫入測試報告 runner.run(suit) else: print("Have no case to test.") except Exception as ex: print(str(ex)) # log.info(str(ex)) finally: print("*********TEST END*********") # log.info("*********TEST END*********") fp.close() # 判斷郵件傳送的開關 if on_off == 'on': send_mail.outlook() else: print("郵件傳送開關配置關閉,請開啟開關後可正常自動傳送測試報告") 2. readConfig.py 讀取配置檔案 程式碼如下:
import os import configparser from config import getpathInfo path = getpathInfo.get_Path()# 專案路徑 config_path = os.path.join(path, 'config.ini')# config.ini的絕對路徑 config_uri_path = os.path.join(path, 'config_uri.ini')# config_uri.ini的絕對路徑 config = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法 config_uri = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法 config.read(config_path, encoding='utf-8') config_uri.read(config_uri_path, encoding='utf-8') class ReadConfig(): def get_http(self, name): value = config.get('HTTP', name) return value def get_email(self, name): value = config.get('EMAIL', name) return value def get_mysql(self, name): value = config.get('DATABASE', name) return value def get_static_params(self,name): value = config.get("StaticParams",name) return value def get_interface_uri(self,name): value = config_uri.get("URI",name) return value def get_common_params(self,name): path = getpathInfo.get_Path() # 呼叫例項化,還記得這個類返回的路徑為 config_path = os.path.join(path, 'config.ini') # 這句話是在path路徑下再加一級,最後變成 config = configparser.ConfigParser() # 呼叫外部的讀取配置檔案的方法 config.read(config_path, encoding='utf-8') value = config.get("CommonParams",name) print(value) # logger.info("=========readconfig===="+value) return value def get_personal_information(self,name): value = config.get("PersonalInformation", name) # print(value) # logger.info("=========readconfig===="+value) return value if __name__ == '__main__':# 測試一下,我們讀取配置檔案的方法是否可用 print('HTTP中的baseurl值為:', ReadConfig().get_http('baseurl')) print('EMAIL中的開關on_off值為:', ReadConfig().get_email('on_off')) print('StaticParams中的開關clientIp值為:', ReadConfig().get_static_params('clientIp'))
2.1 靜態呼叫
# 專案路徑 path = getpathInfo.get_Path() # config.ini的絕對路徑 config_path = os.path.join(path, 'config.ini') # config_uri.ini的絕對路徑 config_uri_path = os.path.join(path, 'config_uri.ini') # 呼叫外部的讀取配置檔案的方法 config = configparser.ConfigParser() config_uri = configparser.ConfigParser() # utf-8的方式讀取配置檔案 config.read(config_path, encoding='utf-8') config_uri.read(config_uri_path, encoding='utf-8') 2.2 具體實現
[StaticParams] clientmac = 9e:ee:fb:0f:5b:b8 clientdensity = 3.0 latitude = 255 longitude = 255 isjailbroken = 0 jailreason = NO Jail clientversion = 3.2.5 deviceid = 9e:ee:fb:0f:5b:b8 platform = Android deviceinfo = {"deviceModel":"MI 5","deviceOs":"23_6.0.1"} network = wifi screensize = 1920*1080 clientip = 10.0.3.15
讀取程式碼
class ReadConfig(): # 封裝讀取靜態引數 def get_static_params(self,name): value = config.get("StaticParams",name) return value
3. 寫入配置檔案
import os,sys import configparser from config import getpathInfo path = getpathInfo.get_Path()# 呼叫例項化,還記得這個類返回的路徑為 config_path = os.path.join(path, 'config.ini')# 這句話是在path路徑下再加一級,最後變成 config = configparser.ConfigParser()# 呼叫外部的讀取配置檔案的方法 config.read(config_path, encoding='utf-8') class WriteConfig(): def write_common_params(self,token,id,username,secret): # print(config.options("CommonParams")) config.set("CommonParams",'customer_token',token) config.set("CommonParams",'user_id',id) config.set("CommonParams",'username',username) config.set("CommonParams",'secret',secret) # logger.info("======writeconfig======"+token) with open(config_path,'w',encoding="utf-8") as f: config.write(f) # sys.stdout.flush() if __name__ == '__main__':# 測試一下,我們讀取配置檔案的方法是否可用 WriteConfig().write_common_params("123","哈哈","1232323")
4. Log.py日誌檔案
程式碼如下:
import os import logging from logging.handlers import TimedRotatingFileHandler from config import getpathInfo path = getpathInfo.get_Path() log_path = os.path.join(path, 'result') # 存放log檔案的路徑 class Logger(object): def __init__(self, logger_name='logs…'): self.logger = logging.getLogger(logger_name) logging.root.setLevel(logging.NOTSET) self.log_file_name = 'logs' # 日誌檔案的名稱 self.backup_count = 5 # 最多存放日誌的數量 # 日誌輸出級別 self.console_output_level = 'WARNING' self.file_output_level = 'DEBUG' # 日誌輸出格式 self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') def get_logger(self): """在logger中新增日誌控制代碼並返回,如果logger已有控制代碼,則直接返回""" if not self.logger.handlers: # 避免重複日誌 console_handler = logging.StreamHandler() console_handler.setFormatter(self.formatter) console_handler.setLevel(self.console_output_level) self.logger.addHandler(console_handler) # 每天重新建立一個日誌檔案,最多保留backup_count份 file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D', interval=1, backupCount=self.backup_count, delay=True, encoding='utf-8') file_handler.setFormatter(self.formatter) file_handler.setLevel(self.file_output_level) self.logger.addHandler(file_handler) return self.logger logger = Logger().get_logger()
5. 裝飾器提取響應,類似Java中的getter和setter方法
__author__ = 'csjin' # 定義@property裝飾器 class Login(object): @property def code(self): return self.__code @code.setter def code(self,code): self.__code = code @property def msg(self): return self.__msg @msg.setter def msg(self,msg): self.__msg = msg @property def customer_token(self): return self.__token @customer_token.setter def customer_token(self,token): self.__token = token