文章目錄
- 一、如何實現一條用例,實現覆蓋所有用例的測試
- 1、結合數據驅動:編輯一條用例,外部導入數據實現循環測試
- 2、用例體:實現不同用例的操作步驟+對應的斷言
- 二、實戰
- 1、項目路徑總覽
- 2、common 文件夾下的代碼文件
- 3、keywords 文件夾下的代碼文件
- 4、testcases 文件夾下的代碼文件
- 4、testdata 文件夾下的 case_data.xlsx 文件
- 5、config.py 文件
- 三、web 自動化測試完結-項目代碼(總)
一、如何實現一條用例,實現覆蓋所有用例的測試
1、結合數據驅動:編輯一條用例,外部導入數據實現循環測試
2、用例體:實現不同用例的操作步驟+對應的斷言
- 封裝對應的方法,可以執行所有用例的操作步驟+斷言
二、實戰
自動化測試框架實現與意義:
- 通過讀取 excel 數據,進行數據驅動自動化測試
- 通過反射函數,實現關鍵字驅動
- 即使不懂代碼的人,也能通過編輯 excel 數據進行測試
下述項目基本實現:
- 先通過讀取用例匯總統計表中的“是否執行”來加載要執行的測試用例
- 通過要執行的測試用例名稱來找到對應的工作表
- 按照測試用例工作表中的測試步驟,來通過反射函數進行一一執行
1、項目路徑總覽
2、common 文件夾下的代碼文件
2.1 excel_operator.py 文件
是對 excel 操作進行封裝的方法
import os.path
import timeimport openpyxl
from TestKDT import config
from openpyxl.styles import Font,PatternFill,colorsclass ExcelOperator:"""操作 excel 文件"""def __init__(self,filename=os.path.join(config.testdata_dir,"case_data.xlsx")):# 獲取到測試用例 excel 的文件路徑self.file_path = filename# 獲取到測試用例 excel 工作簿self.wk = openpyxl.load_workbook(filename)def get_case_data(self):# """獲取 excel 工作簿中所有工作表的數據"""# self.sheetnames = self.wk.sheetnames"""獲取 excel 工作簿中要執行的用例工作表的數據"""self.sheetnames = self.get_cases_name()values= []# 循環每個工作表for sheet_name in self.sheetnames:# 獲取某個工作表某個區間列的數據value = self.get_startcol_endcol_value(sheet_name)case_data = {'case_name': sheet_name, 'steps_data': value}values.append(case_data)return values# 獲取執行用例的工作表名稱def get_cases_name(self):# 獲取匯總用例的工作表cases_sheet = self.wk[config.cases_sheet_name]cases_name = []# 根據是否執行,取到用例的名稱for row in range(2,cases_sheet.max_row+1):# 循環匯總表每一行數據is_execute = cases_sheet.cell(row, config.case_is_execute).valueif is_execute=="y":case_name = cases_sheet.cell(row, config.case_name).valuecases_name.append(case_name)return cases_name# 獲取某個工作表某個區間列的數據def get_startcol_endcol_value(self,sheetname,startcol=config.keyword_col,endcol=config.action_col):# 獲取工作表sheet = self.wk[sheetname]values = []# 循環對應工作表中的每一行數據for row in range(2,sheet.max_row+1):step_data = []for col in range(startcol,endcol+1):value = sheet.cell(row=row,column=col).valueif value is not None:step_data.append(value)values.append(step_data)return values# 將測試步驟的結果寫入 excel 文件的用例工作表中def write_step_result(self, sheet_name, row, col, result):"""寫入測試步驟的結果"""case_sheet = self.wk[sheet_name]# 寫入每步操作步驟結束時間self.write_current_time(case_sheet,row,config.step_end_time)# 寫入測試步驟的結果case_sheet.cell(row, col).value = result# 顏色填充 綠色通過 紅色失敗red_fill = PatternFill(fill_type="solid", fgColor="00FF0000")green_fill = PatternFill(fill_type="solid", fgColor="0000FF00")if result == 'FAIL':case_sheet.cell(row, col).fill = red_fillelse:case_sheet.cell(row, col).fill = green_fillself.wk.save(self.file_path)# 將測試用例的結果寫入 excel 文件的用例匯總表中def write_cases_result(self, case_name, sheet_name=config.cases_sheet_name, col=config.case_result,):"""寫入測試用例的結果"""cases_sheet = self.wk[sheet_name]for row in range(2,cases_sheet.max_row+1):# 循環匯總表的每一行col_case_name = cases_sheet.cell(row, config.case_name).valueif col_case_name == case_name:# 獲取執行的用例case_name = cases_sheet.cell(row, config.case_name).valuesheet = self.wk[case_name]# 寫入每條測試用例的結束時間self.write_current_time(cases_sheet, row, config.case_end_time)# 獲取每條執行用例的結果steps_result = self.get_sheet_col_value(sheet=sheet, col=config.step_result)# 顏色填充 綠色通過 紅色失敗red_fill = PatternFill(fill_type="solid", fgColor="00FF0000")green_fill = PatternFill(fill_type="solid", fgColor="0000FF00")# 將結果寫入匯總表中if 'FAIL' in steps_result:cases_sheet.cell(row, col).value = 'FAIL'cases_sheet.cell(row, col).fill = red_fillelse:cases_sheet.cell(row, col).value = 'PASS'cases_sheet.cell(row, col).fill = green_fillself.wk.save(self.file_path)def get_sheet_col_value(self, sheet, col):"""獲取某個工作表某列的所有數據"""values = []for row in range(2, sheet.max_row + 1):step_col_value = sheet.cell(row, col).valuevalues.append(step_col_value)return valuesdef write_current_time(self, sheet, row, col):"""將當前時間寫入某個表格中"""current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())sheet.cell(row, col).value = current_timeif __name__ == '__main__':# print(ExcelOperator().get_startcol_endcol_value("登錄",3,6))print(ExcelOperator().get_cases_name())
2.2、logger.py 文件
和之前 POM 的日志一樣
import logging
import os
import time
from TestKDT import configclass FrameLogger:def get_logger(self):# 創建日志器logger = logging.getLogger("logger")# 日志輸出當前級別及以上級別的信息,默認日志輸出最低級別是warningif not logger.handlers:logger.setLevel(logging.INFO)# 創建控制臺處理器----》輸出控制臺SH = logging.StreamHandler()# 創建文件處理器----》輸出文件log_path = os.path.join(config.logs_dir, f"log_{time.strftime('%Y%m%d%H%M%S', time.localtime())}.txt")FH = logging.FileHandler(log_path,mode="w",encoding="utf-8")# 日志包含哪些內容 時間 文件 日志級別 :事件描述/問題描述formatter = logging.Formatter(fmt="[%(asctime)s] [%(filename)s] %(levelname)s :%(message)s",datefmt='%Y/%m/%d %H:%M:%S')logger.addHandler(SH)logger.addHandler(FH)SH.setFormatter(formatter)FH.setFormatter(formatter)return logger
3、keywords 文件夾下的代碼文件
3.1 library.py 文件
是對各種關鍵字函數的封裝
import os
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from TestKDT import config
from TestKDT.common.logger import FrameLogger as logclass Library:# 登錄用例 = 多個操作步驟組成 基于每個操作步驟封裝對應的關鍵字函數# 登錄用例:# 1、打開瀏覽器- 關鍵字函數- open_browser()# 2、加載項目地址- 關鍵字函數- load_url()# 3、輸入用戶名- 關鍵字函數- input()# 4、輸入密碼- 關鍵字函數- input()# 5、點擊登錄- 關鍵字函數- click()def __init__(self):self.logger = log().get_logger()def open_browser(self,browser):"""打開瀏覽器"""# 傳入的瀏覽器參數保持首字母大寫browser = browser.capitalize()# 獲取不同類型的瀏覽器驅動try:self.driver = getattr(webdriver, browser)()self.logger.info(f"打開{browser}瀏覽器成功")except:self.logger.error(f"打開{browser}瀏覽器失敗")raisedef load_url(self, url):"""加載地址"""try:self.driver.get(url)self.logger.info(f"加載項目地址{url}成功")except:self.logger.error(f"加載項目地址{url}失敗")raise# 等待元素可見def wait_ele_visibility(self, page_name, loc, timeout=15, poll_fre=0.5):try:WebDriverWait(self.driver, timeout, poll_fre).until(EC.visibility_of_element_located(loc))# WebElement對象self.logger.info(f"在[{page_name}]頁面,找到元素:{loc}可見")except:self.logger.error(f"在[{page_name}]頁面,未找到元素:{loc}可見!!!")raise# 等待元素存在def wait_ele_presence(self, page_name, loc, timeout=15, poll_fre=0.5):try:WebDriverWait(self.driver, timeout, poll_fre).until(EC.presence_of_element_located(loc))self.logger.info(f"在[{page_name}]頁面,找到元素:{loc}存在")except:self.logger.error(f"在[{page_name}]頁面,未找到元素:{loc}存在!!!")raisedef locator(self, page_name, by_type, express):# 定位元素try:el = self.driver.find_element(by_type, express)self.logger.info(f"在[{page_name}]頁面,通過[{by_type}]方法和[{express}]語句,定位元素成功")except:self.save_screenshot(by_type)self.logger.error(f"在[{page_name}]頁面,通過[{by_type}]方法和[{express}]語句,定位元素失敗!!!")raisereturn eldef input(self, page_name, by_type, express, text):"""輸入"""loc = (by_type, express)try:self.wait_ele_visibility(page_name, loc)self.locator(page_name,by_type, express).send_keys(text)self.logger.info(f"在[{page_name}]頁面,元素{loc}輸入:{text} 成功!")except:self.logger.error(f"在[{page_name}]頁面,元素{loc}輸入失敗!")# 失敗截圖self.save_screenshot(page_name)raisedef click(self, page_name, by_type, express):"""點擊"""loc = (by_type, express)try:self.wait_ele_visibility(page_name, loc)self.locator(page_name,by_type, express).click()self.logger.info(f"在[{page_name}]頁面,元素{loc}點擊成功!")except:self.logger.error(f"在[{page_name}]頁面,元素{loc}點擊失敗!")# 失敗截圖self.save_screenshot(page_name)raisedef move_element(self, page_name, by_type, express):"""移動鼠標"""loc = (by_type, express)try:self.wait_ele_visibility(page_name, loc)el = self.locator(page_name, by_type, express)# 將鼠標移到元素上ActionChains(self.driver).move_to_element(el).perform()self.logger.info(f"在[{page_name}]頁面,鼠標移到元素{loc}成功!")except:self.logger.error(f"在[{page_name}]頁面,鼠標移到元素{loc}失敗!")# 失敗截圖self.save_screenshot(page_name)raisedef assert_text(self,page_name, by_type, express, expect):"""斷言"""loc = (by_type, express)try:self.wait_ele_visibility(page_name, loc)el = self.locator(page_name, by_type, express)fact = el.textself.logger.info(f"在[{page_name}]頁面,元素{loc}文本內容獲取成功!")except:self.logger.error(f"在[{page_name}]頁面,元素{loc}文本內容獲取失敗!")# 失敗截圖self.save_screenshot(page_name)raiseif fact == expect:passelse:raise Exception(f"在[{page_name}]頁面,斷言失敗,assertText:{fact} != {expect}")def save_screenshot(self,img_name):file_name = os.path.join(config.screenshots_dir, img_name+'.png')self.driver.save_screenshot(file_name)self.logger.error(f"失敗截圖,截取當前網頁,存儲的路徑:{file_name}")# 封裝方法,可以調用當前類下的所有關鍵字函數# *args:不定長參數def run(self,keyword,*args):print(keyword, args)# 實現打開瀏覽器# keyword = "open_browser"# args = ("edge",)# 調用關鍵字函數,基于反射getattr(self, keyword)(*args)
4、testcases 文件夾下的代碼文件
4.1 test_case.py 文件
編寫測試用例
import time
import unittest
from selenium.webdriver.common.by import By
from TestKDT.keywords.library import Library
from ddt import ddt,data,file_data,unpack
from TestKDT.common.excel_operator import ExcelOperator
from TestKDT import config@ddt
class TestCase01(unittest.TestCase):# 如何實現一條用例,實現覆蓋所有用例的測試# 每條用例的數據: 關鍵字函數 + 測試數據# case_data = [["open_browser","edge"],["load_url","http://116.62.63.211/shop/user/logininfo.html"],# ["input",(By.NAME, "accounts"), "hc_test"],# ["input",(By.XPATH, '//input[@type="password"]'),"hctest123"],# ["click",(By.XPATH, '//button[text()="登錄"]')]]excel = ExcelOperator()case_data = excel.get_case_data()@data(*case_data)def test_cases(self,case_data):# 打印每條用例的數據print(case_data)case_name = case_data['case_name']steps_data = case_data['steps_data']# 用例體 = 操作步驟+ 斷言# 封裝對應的方法,可以執行所有用例的操作步驟 + 斷言lib = Library()for index, step_data in enumerate(steps_data):try:# 執行用例的操作步驟lib.run(*step_data)# 當前執行步驟為 PASS# index 0 行數 2 因為第一行不是測試數據,而是列說明self.excel.write_step_result(sheet_name=case_name, row=index+2, col=config.step_result, result="PASS")except Exception as error:# 當前執行步驟為 FAILself.excel.write_step_result(sheet_name=case_name, row=index+2, col=config.step_result, result="FAIL")self.excel.write_cases_result(case_name)
4、testdata 文件夾下的 case_data.xlsx 文件
- 4.1 用例匯總統計
- 4.2 登錄成功
- 4.3 登錄失敗-1
- 4.4 登錄失敗-2
5、config.py 文件
是項目的路徑以及其他數據內容
import os# 根路徑
base_dir = os.path.dirname(os.path.abspath(__file__))
# 用例路徑
testcases_dir = os.path.join(base_dir, 'testcases')
# 數據路徑
testdata_dir = os.path.join(base_dir, 'testdata')
# 測試報告路徑
reports_dir = os.path.join(base_dir, 'outputs/reports')
# 日志路徑
logs_dir = os.path.join(base_dir, 'outputs/logs')
# 失敗截圖
screenshots_dir = os.path.join(base_dir, 'outputs/screenshots')
# 測試用例 excel 中關鍵字所在列
keyword_col = 3
# 測試用例 excel 中操作值所在列
action_col = 7
# 用例匯總表名稱
cases_sheet_name = "用例匯總統計"
# 用例匯總表中是否執行所在列數
case_is_execute = 5
# 用例匯總表中用例名所在列數
case_name = 2
# 用例匯總表中測試結果所在列數
case_result = 7
# 用例匯總表中測試結束時間所在列數
case_end_time = 6
# 用例工作表中執行結果所在列數
step_result = 9
# 用例工作表中執行時間所在列數
step_end_time = 8# ---調試
print(testdata_dir)
三、web 自動化測試完結-項目代碼(總)
Test-Web.zip