一、什么是配置文件?
-
配置文件示例
[mysql] default-character-set = utf8 [mysqld] port = 3306 basedir = c:\mysql-5.7.25-winx64\mysql-5.7.25-winx64 daradir = c:\mysql-5.7.25-winx64\mysql-5.7.25-winx64\data max_connections = 200 character-set-server = utf8 default-storage-engine = INNODB explicit_defaults_for_timestamp = true
-
為什么要做配置文件?
所有的代碼和配置都變成模塊化可配置化,這樣就提高了代碼的復用性,不用每次都去修改代碼內部。
-
場景舉例
1、多處地方需要使用同一個參數,這時候最好是配置化,這樣改一處就可以了
2、如果是經常變化的變量,我們也可以做這個配置。---> 與參數化要區分開來
-
python中常見的配置文件格式
.ini、.conf、.cfg結尾的文件
-
配置對象
- section
- option
二、ConfigParser模塊
掌握一些比較基礎的用法:跟file文件一樣,要先打開才能進行讀取操作
-
實例化ConfigParse對象:cf = configParser.ConfigParser()
-
打開配置文件:cf.read(配置文件名稱或者地址)
-
常用方法:
-
- read(filename) 直接讀取文件內容
- sections() 得到所有的section,並以列表的形式返回
- options(section) 得到該section的所有option
- items(section) 得到該section的所有鍵值對
- get(section,option) 得到該section中option的值,返回類型為string
- getint(section,option) 得到該section中option的值,返回為int類型,還有相應的getboolean()和getfloat()
-
練習
import configparser # 實例化ConfigParser對象 conf = configparser.ConfigParser() # 打開配置文件 cf = conf.read("my.conf",encoding = 'utf8') # 根據section和option得到option的值 a = conf.get('mysqld','port') print(a) # 得到所有的section,並以列表的形式返回 b = conf.sections() print(b) # 得到該section的所有option c = conf.options('mysqld') print(c) # 得到該section所有的鍵值對 d = dict(conf.items("mysql")) print(d)
輸出結果為:
-
封裝一個讀取配置文件的類
import configparser """ 為什么要封裝? 封裝是為了使用起來更加方便,便於再次修改 封裝的需求? 封裝成什么樣子才能達到我們的目的 封裝的原則: 寫死的固定數據(變量),可以封裝成雷屬性 實現某個功能的代碼封裝成方法 在各個辦法中都要用到的數據,抽離出來作為實例屬性 """ # 封裝前 讀取數據(三部曲) # 實例化ConfigParser對象 # conf = configparser.ConfigParser() # 打開配置文件 # conf.read("config.ini",encoding='utf8') # # 根據section和option得到option的值 # conf.get('excel','file_name') class ReadConfig(configparser.ConfigParser): def __init__(self): # 實例化對象 super().__init__() # 加載文件 self.read(r"E:\python_api_test\API_Test_v4_config\conf\config.ini",encoding='utf8') conf = ReadConfig()
-
在其他模塊調用封裝好的配置文件時:from xxxx.xxxx.config import conf
三、將配置文件集成到單元測試中
1、項目結構
- common:這個目錄用來存放的是自己封裝的類
- conf:這個目錄用來存放配置文件
- librarys:這個目錄用來存放已封裝好的模塊(HTMLtestrunner、ddt)
- logs:這個目錄用來存放日志文件
- data:這個目錄用來存放excel的測試用例數據
- reposts:這個目錄用來存放生成的的測試報告
- testcases:這個目錄用來存放所有的測試用例模塊
2、各項目層的結構圖
3、各項目層的具體代碼
-
common層(包括config.py文件、mylogging_v3.py文件、read_excel_data.py文件)
新建config.py文件
import configparser """ 為什么要封裝? 封裝是為了使用起來更加方便,便於再次修改 封裝的需求? 封裝成什么樣子才能達到我們的目的 封裝的原則: 寫死的固定數據(變量),可以封裝成雷屬性 實現某個功能的代碼封裝成方法 在各個辦法中都要用到的數據,抽離出來作為實例屬性 """ # 封裝前 讀取數據(三部曲) # 實例化ConfigParser對象 # conf = configparser.ConfigParser() # 打開配置文件 # conf.read("config.ini",encoding='utf8') # # 根據section和option得到option的值 # conf.get('excel','file_name') class ReadConfig(configparser.ConfigParser): def __init__(self): # 實例化對象 super().__init__() # 加載文件 self.read(r"E:\python_api_test\API_Test_v4_config\conf\config.ini",encoding='utf8') conf = ReadConfig()
新建mylogging_v3.py文件
import logging from API_Test_v4_config.common.config import conf # 日志收集器的名稱 logger_name = conf.get('log','logger_name') # 日志收集器的級別 level = conf.get('log','level').upper() # 輸出到控制台的日志級別 sh_level = conf.get('log','sh_level').upper() # 輸出到文件的日志級別 fh_level = conf.get('log','fh_level').upper() # 日志保存的文件 log_file_path = conf.get('log','log_file_path') class MyLogging(object): def create_logger(*args,**kwargs): # 創建自己的日志收集器 my_log = logging.getLogger(logger_name) # 設置收集的日志等級,設置為DEBUG等級 my_log.setLevel(level) # 日志輸出渠道 # 創建一個日志輸出渠道(輸出到控制台),並且設置輸出的日志等級為INFO以上 l_s = logging.StreamHandler() l_s.setLevel(sh_level) # 創構建一個日志輸出渠道(輸出到文件),並且設置輸出的日志等級為DEBUG以上 l_f = logging.FileHandler(log_file_path,encoding='utf8') l_f.setLevel(fh_level) #將日志輸出渠道添加到日志收集器中 my_log.addHandler(l_s) my_log.addHandler(l_f) # 設置日志輸出的格式 ft = "%(asctime)s - [%(filename)s -->line:%(lineno)d] - %(levelname)s: %(message)s" ft = logging.Formatter(ft) # 設置控制台和日志文件輸出日志的格式 l_s.setFormatter(ft) l_f.setFormatter(ft) return my_log def debug(self,msg): self.my_log.debug(msg) def info(self,msg): self.my_log.info(msg) def warning(self,msg): self.my_log.warning(msg) def error(self,msg): self.my_log.error(msg) def critical(self,msg): self.my_log.critical(msg) #日志輸出 m_log = MyLogging() # 創建日志收集器 logger = m_log.create_logger()
新建read_excel_data.py文件
import openpyxl class Case: def __init__(self,arrts): for item in arrts: setattr(self,item[0],item[1]) class ReadExcel(object): def __init__(self,filename,sheetname): """ 定義需要打開的文件及表名 :param filename: 文件名 :param sheetname: 表名 """ self.wb = openpyxl.load_workbook(filename) # 打開工作簿 self.sheet = self.wb[sheetname] # 選定表單 self.filename = filename # 特殊的魔術方法,在對象銷毀之后執行的 def __del__(self): # 關閉文件 self.wb.close() def read_data_line(self): #按行讀取數據轉化為列表 rows_data = list(self.sh.rows) # print(rows_data) # 獲取表單的表頭信息 titles = [] for title in rows_data[0]: titles.append(title.value) # print(titles) #定義一個空列表用來存儲測試用例 cases = [] for case in rows_data[1:]: # print(case) data = [] for cell in case: #獲取一條測試用例數據 # print(cell.value) data.append(cell.value) # print(data) #判斷該單元格是否為字符串,如果是字符串類型則需要使用eval();如果不是字符串類型則不需要使用eval() if isinstance(cell.value,str): data.append(eval(cell.value)) else: data.append(cell.value) #將該條數據存放至cases中 # print(dict(list(zip(titles,data)))) case_data = dict(list(zip(titles,data))) cases.append(case_data) return cases def read_excel_obj_new(self,list1): """ 按指定的列,讀取excel中的數據,以列表的形式返回,列表中每個對象為一條測試用例, Excel中的表頭為對象的屬性,對應的數據為屬性值。 :param list1: list --->要讀取的列[1,2,3...] :return: type:list--->[case_obj1,case_obj2.......] """ # 從配置文件中讀取的數據類型為string,需要轉化為list list1 = eval(list1) # 判斷傳入的讀取數據的列數是否為空,為空的話直接讀取excel中所有的數據。 if list1 == []: return self.read_data_line() # 獲取表里面的最大行數 max_row = self.sheet.max_row # 定義一個空列表,用來存放測試用例數據 cases = [] # 定義一個空列表,用來存放表頭數據 titles = [] # 遍歷所有的行數據 for row in range(1,max_row+1): case_data = [] if row != 1: for column in list1: info = self.sheet.cell(row,column).value # print(info) case_data.append(info) case = list(zip(titles,case_data)) # print(case) case_obj = Case(case) cases.append(case_obj) else: for column in list1: title = self.sheet.cell(row,column).value titles.append(title) if None in titles: raise ValueError("表頭的數據有顯示為空") return cases def write_excel(self,row,column,msg): #寫入數據 self.sheet.cell(row=row,column=column,value=msg) self.wb.save(self.filename)
-
conf層(config.ini文件--->配置文件)
新建config.ini文件
# log日志相關配置 [log] # 日志收集器的名稱 logger_name = my_log # 日志收集器的級別 level = DEBUG # 輸出到控制台的日志級別 sh_level = DEBUG # 輸出到文件的日志級別() fh_level = debug # 日志保存的文件 log_file_path = E:\python_api_test\API_Test_v4_config\logs\log.log # 讀取excel中測試用例數據相關的配置 [excel] # 用例文件名稱 file_name = E:\python_api_test\API_Test_v4_config\data\cases.xlsx # sheet表單名稱 sheet_name = Sheet1 # 讀取表單中的列數(每條用例的數據) []空列表便是所有列 read_colums = [1,2,3] # 測試報告相關的配置 [report] report_path = E:\python_api_test\API_Test_v4_config\reports\report.html report_name = python接口自動化測試報告 report_tester = 測試
-
data層(cases.xlsx文件--->測試用例數據)
新建cases.xlsx文件
-
testcase層(register_testcase.py文件--->注冊函數的測試用例)
新建register_testcase.py文件
import unittest from API_Test_v4_config.register import register from API_Test_v4_config.common.read_excel_data import ReadExcel from ddt import ddt,data from API_Test_v4_config.common.mylogging_v3 import logger from API_Test_v4_config.common.config import conf # 配置文件中讀取excel相關數據 file_name = conf.get('excel','file_name') sheet_name = conf.get('excel','sheet_name') read_colums = conf.get('excel','read_colums') # 讀取excel中的數據 wb = ReadExcel(file_name,sheet_name) cases = wb.read_excel_obj_new(read_colums) @ddt class RegisterTestCase(unittest.TestCase): def setUp(self): print("准備測試環境,執行測試用例之前會執行此操作") def tearDown(self): print("還原測試環境,執行完測試用例之后會執行此操作") @data(*cases) def test_register(self,case): self.row = case.caseid + 1 res = register(*eval(case.data)) try: self.assertEquals(eval(case.excepted),res) except AssertionError as e: res = "未通過" logger.error(e) raise e else: res = "通過" logger.info("該條測試用例的測試結果為:{}".format(res)) finally: # 調用寫入數據的方法,在excel中回寫測試用例的執行結果 wb.write_excel(row=self.row,column=4,msg=res) if __name__ == '__main__': unittest.main()
-
最外面層(register.py文件--->需要測試的功能函數、 register_suites.py--->執行測試套件)
新建register.py文件
# 設計用例,對注冊功能函數進行單元測試 users = [{'user': 'python18', 'password': '123456'}] def register(username, password1, password2): # 注冊功能 for user in users: # 遍歷出所有賬號,判斷賬號是否存在 if username == user['user']: # 賬號存在 return {"code": 0, "msg": "該賬戶已存在"} else: if password1 != password2: # 兩次密碼不一致 return {"code": 0, "msg": "兩次密碼不一致"} else: # 賬號不存在 密碼不重復,判斷賬號密碼長度是否在 6-18位之間 if 6 <= len(username) <= 18 and 6 <= len(password1) <= 18: # 注冊賬號 users.append({'user': username, 'password': password2}) return {"code": 1, "msg": "注冊成功"} else: # 賬號密碼長度不對,注冊失敗 return {"code": 0, "msg": "賬號和密碼必須在6-18位之間"}
新建register_suites.py文件
import unittest from HTMLTestRunnerNew import HTMLTestRunner from API_Test_v4_config.testcases import register_testcase from API_Test_v4_config.common.config import conf # 第二步:創建測試套件 suite = unittest.TestSuite() # 第三步:將測試用例加載到測試套件中 loader = unittest.TestLoader() # 通過測試用例類來添加測試用例 # suite.addTest(loader.loadTestsFromTestCase(RegisterTestCase)) # 通過模塊來添加測試用例 suite.addTest(loader.loadTestsFromModule(register_testcase)) # 添加測試用例,通過路徑加載測試用例目錄下的所有模塊 # suite.addTest(loader.discover("E:\\python_api_test\\API_Test_v4_config\\testcases")) #第四步:執行測試套件,生成測試報告 # 讀取配置文件中report相關的配置信息 report_path = conf.get("report",'report_path') report_name = conf.get('report','report_name') report_tester = conf.get('report','report_tester') with open(report_path,'wb') as f: runner = HTMLTestRunner( stream = f, verbosity = 2, title = 'python_18_report', description = report_name, tester = report_tester ) runner.run(suite)