UI自动化测试框架:PO模式+数据驱动

UI自动化测试框架:PO模式+数据驱动,第1张

UI自动化测试框架:PO模式+数据驱动 1. PO 设计模式简介 2. 工程结构说明 3. 工程代码实现
  • page 包
  • action 包
  • business_process 包
  • util 包
  • conf 包
  • test_data 目录
  • log 目录
  • screenshot_path 目录
  • main.py
1. PO 设计模式简介 什么是 PO 模式?

PO(PageObject)设计模式将某个页面的所有元素对象定位和对元素对象的 *** 作封装成一个 Page 类,并以页面为单位来写测试用例,实现页面对象和测试用例的分离。


PO 模式的设计思想与面向对象相似,能让测试代码变得可读性更好,可维护性高,复用性高。


PO 模式可以把一个页面分为三个层级:对象库层、 *** 作层、业务层。


  1. 对象库层:封装定位元素的方法。


  2. *** 作层:封装对元素的 *** 作。


  3. 业务层:将一个或多个 *** 作组合起来完成一个业务功能。


一条测试用例可能需要多个步骤 *** 作元素,将每一个步骤单独封装成一个方法,在执行测试用例时调用封装好的方法进行 *** 作。


PO 模式的优点
  • 通过页面分层,将测试代码和被测试页面的页面元素及其 *** 作方法进行分离,降低代码冗余。


  • 页面对象与用例分离,业务代码与测试代码分离,降低耦合性。


  • 不同层级分属不同用途,降低维护成本。


  • 代码可阅读性增强,整体流程更为清晰。


2. 工程结构简介 工程结构

整个测试框架分为四层,通过分层的方式,测试代码更容易理解,维护起来较为方便。


第一层是“测试工具层”:

  • util 包:用于实现测试过程中调用的工具类方法,例如读取配置文件、页面元素的 *** 作方法、 *** 作Excel文件等。


  • conf 包:配置文件及全局变量。


  • test_data 目录:Excel 数据文件,包含测试数据输入、测试结果输出。


  • log 目录:日志输出文件。


  • screenshot_path 目录:异常截图保存目录。


第二层是“服务层”,相当于对测试对象的一个业务封装。


对于接口测试,是对远程方法的一个实现;对于页面测试,是对页面元素或 *** 作的一个封装。


  • page 包:对象库层及 *** 作层,将所有页面的元素对象定位及其 *** 作分别封装成一个类。


第三层是“测试用例逻辑层”,该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验。


  • action 包:组装单个用例的流程。


  • business_process 包:基于业务层和测试数据文件,执行测试用例集合。


  • test_data 目录:Excel 数据文件,包含测试数据输入、测试结果输出。


     

第四层是“测试场景层”,将测试用例组织成测试场景,实现各种级别 cases 的管理、冒烟,回归等测试场景。


 

  • main.py:本 PO 框架的运行主入口。


框架特点
  1. 通过配置文件,实现页面元素定位方式和测试代码的分离。


  2. 使用 PO 模式,封装了网页中的页面元素,方便测试代码调用,也实现了一处维护全局生效的目标。


  3. 在 excel 文件中定义多组测试数据,每个登录用户都一一对应一个存放联系人数据的 sheet,测试框架可自动调用测试数据完成数据驱动测试。


  4. 实现了测试执行过程中的日志记录功能,可以通过日志文件分析测试脚本执行的情况。


  5. 在 excel 数据文件中,通过设定“测试数据是否执行”列的内容为 y 或 n,自定义选择测试数据,测试执行结束后会在"测试结果列"中显示测试执行的时间和结果,方便测试人员查看。


3. 工程代码示例

本框架的 github 地址:https://github.com/juno3550/UIPOFramework

page 包

对象库层及 *** 作层,将所有页面的元素对象定位及其 *** 作分别封装成一个类。


login_page.py
 1 from conf.global_var import *
2 from util.ini_parser import IniParser
3 from util.find_element_util import *
4
5
6 # 登录页面元素定位及 *** 作
7 class LoginPage:
8
9 def __init__(self, driver):
10 self.driver = driver
11 # 初始化跳转登录页面
12 self.driver.get(LOGIN_URL)
13 # 初始化指定ini配置文件及指定分组
14 self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_loginPage")
15
16 # 获取frame元素对象
17 def get_frame_obj(self):
18 locate_method, locate_exp = self.cf.get_value("loginPage.frame").split(">")
19 return find_element(self.driver, locate_method, locate_exp)
20
21 # 切换frame
22 def switch_frame(self):
23 self.driver.switch_to.frame(self.get_frame_obj())
24
25 # 获取用户名输入框元素对象
26 def get_username_input_obj(self):
27 locate_method, locate_exp = self.cf.get_value("loginPage.username").split(">")
28 return find_element(self.driver, locate_method, locate_exp)
29
30 # 清空用户名输入框 *** 作
31 def clear_username(self):
32 self.get_username_input_obj().clear()
33
34 # 输入用户名 *** 作
35 def input_username(self, value):
36 self.get_username_input_obj().send_keys(value)
37
38 # 获取密码输入框元素对象
39 def get_pwd_input_obj(self):
40 locate_method, locate_exp = self.cf.get_value("loginPage.password").split(">")
41 return find_element(self.driver, locate_method, locate_exp)
42
43 # 输入密码 *** 作
44 def input_pwd(self, value):
45 self.get_pwd_input_obj().send_keys(value)
46
47 # 获取登录按钮对象
48 def get_login_buttion_obj(self):
49 locate_method, locate_exp = self.cf.get_value("loginPage.loginbutton").split(">")
50 return find_element(self.driver, locate_method, locate_exp)
51
52 # 点击登录按钮 *** 作
53 def click_login_button(self):
54 self.get_login_buttion_obj().click()
home_page.py
 1 from conf.global_var import *
2 from util.ini_parser import IniParser
3 from util.find_element_util import *
4
5
6 # 登录后主页元素定位及 *** 作
7 class HomePage:
8
9 def __init__(self, driver):
10 self.driver = driver
11 # 初始化指定ini配置文件及指定分组
12 self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_homePage")
13
14 # 获取“通讯录”按钮对象
15 def get_contact_button_obj(self):
16 locate_method, locate_exp = self.cf.get_value("homePage.addressLink").split(">")
17 return find_element(self.driver, locate_method, locate_exp)
18
19 # 点击“通讯录”按钮
20 def click_contact_button(self):
21 self.get_contact_button_obj().click()
contact_page.py
 1 from conf.global_var import *
2 from util.ini_parser import IniParser
3 from util.find_element_util import *
4
5
6 # 通讯录页面元素定位及 *** 作
7 class ContactPage:
8
9 def __init__(self, driver):
10 self.driver = driver
11 # 初始化指定ini配置文件及指定分组
12 self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_contactPersonPage")
13
14 # 获取新建联系人按钮对象
15 def get_contact_create_button_obj(self):
16 locate_method, locate_exp = self.cf.get_value("contactPersonPage.createButton").split(">")
17 return find_element(self.driver, locate_method, locate_exp)
18
19 # 点击新建联系人按钮
20 def click_contact_creat_button(self):
21 self.get_contact_create_button_obj().click()
22
23 # 获取姓名输入框对象
24 def get_name_input_obj(self):
25 locate_method, locate_exp = self.cf.get_value("contactPersonPage.name").split(">")
26 return find_element(self.driver, locate_method, locate_exp)
27
28 # 输入姓名 *** 作
29 def input_name(self, value):
30 self.get_name_input_obj().send_keys(value)
31
32 # 获取邮箱输入框对象
33 def get_email_input_obj(self):
34 locate_method, locate_exp = self.cf.get_value("contactPersonPage.email").split(">")
35 return find_element(self.driver, locate_method, locate_exp)
36
37 # 输入邮箱 *** 作
38 def input_email(self, value):
39 self.get_email_input_obj().send_keys(value)
40
41 # 获取星标联系人单选框对象
42 def get_star_button_obj(self):
43 locate_method, locate_exp = self.cf.get_value("contactPersonPage.starContacts").split(">")
44 return find_element(self.driver, locate_method, locate_exp)
45
46 # 点击星标联系人 *** 作
47 def click_star_button(self):
48 self.get_star_button_obj().click()
49
50 # 获取手机输入框对象
51 def get_phone_input_obj(self):
52 locate_method, locate_exp = self.cf.get_value("contactPersonPage.phone").split(">")
53 return find_element(self.driver, locate_method, locate_exp)
54
55 # 输入邮箱 *** 作
56 def input_phone(self, value):
57 self.get_phone_input_obj().send_keys(value)
58
59 # 获取备注输入框对象
60 def get_remark_input_obj(self):
61 locate_method, locate_exp = self.cf.get_value("contactPersonPage.otherinfo").split(">")
62 return find_element(self.driver, locate_method, locate_exp)
63
64 # 输入邮箱 *** 作
65 def input_remark(self, value):
66 self.get_remark_input_obj().send_keys(value)
67
68 # 获取确定按钮对象
69 def get_confirm_button_obj(self):
70 locate_method, locate_exp = self.cf.get_value("contactPersonPage.confirmButton").split(">")
71 return find_element(self.driver, locate_method, locate_exp)
72
73 # 点击星标联系人 *** 作
74 def click_confirm_button(self):
75 self.get_confirm_button_obj().click()
action 包

业务层,将一个或多个 *** 作组合起来完成一个业务功能。


case_action.py
from selenium import webdriver
import traceback
import time
from page.contact_page import ContactPage
from page.home_page import HomePage
from page.login_page import LoginPage
from conf.global_var import *
from util.log_util import * # 初始化浏览器
def init_browser(browser_name):
if browser_name.lower() == "chrome":
driver = webdriver.Chrome(CHROME_DRIVER)
elif browser_name.lower() == "firefox":
driver = webdriver.Firefox(FIREFOX_DRIVER)
elif browser_name.lower() == "ie":
driver = webdriver.Ie(IE_DRIVER)
else:
return "Error browser name!"
return driver def assert_word(driver, text):
assert text in driver.page_source # 登录流程封装
def login(driver, username, pwd, assert_text):
login_page = LoginPage(driver)
login_page.switch_frame()
login_page.clear_username()
login_page.input_username(username)
login_page.input_pwd(pwd)
login_page.click_login_button()
time.sleep(1)
assert_word(driver, assert_text) # 添加联系人流程封装
def add_contact(driver, name, email, phone, is_star, remark, assert_text):
home_page = HomePage(driver)
home_page.click_contact_button()
contact_page = ContactPage(driver)
contact_page.click_contact_creat_button()
contact_page.input_name(name)
contact_page.input_email(email)
contact_page.input_phone(phone)
contact_page.input_remark(remark)
if is_star == "是":
contact_page.click_star_button()
contact_page.click_confirm_button()
time.sleep(2)
assert_word(driver, assert_text) def quit(driver):
driver.quit() if __name__ == "__main__":
driver = init_browser("chrome")
login(driver, "zhangjun252950418", "zhangjun123", "退出")
add_contact(driver, "铁蛋", "[email protected]", "12222222222", "是", "这是备注", "铁蛋")
# quit(driver)
business_process 包

基于业务层和测试文件,实现数据驱动的测试执行脚本。


batch_login_process.py
 1 from action.case_action import *
2 from util.excel_util import *
3 from conf.global_var import *
4 from util.datetime_util import *
5 from util.screenshot import take_screenshot
6
7
8 # 封装测试数据文件中用例的执行逻辑
9 # 测试数据文件中的每个登录账号
10 def batch_login(test_data_file, browser_name, account_sheet_name):
11 excel = Excel(test_data_file)
12 # 获取登录账号sheet页数据
13 excel.change_sheet(account_sheet_name)
14 account_all_data = excel.get_all_row_data()
15 account_headline_data = account_all_data[0]
16 for account_row_data in account_all_data[1:]:
17 # 执行登录用例
18 account_row_data[ACCOUNT_TEST_TIME_COL] = get_english_datetime()
19 if account_row_data[ACCOUNT_IS_EXECUTE_COL].lower() == "n":
20 continue
21 # 初始化浏览器
22 driver = init_browser(browser_name)
23 try:
24 # 默认以"退出"作为断言关键字
25 login(driver, account_row_data[ACCOUNT_USERNAME_COL], account_row_data[ACCOUNT_PWD_COL], "退出")
26 info("登录成功【用户名:{}, 密码:{}, 断言关键字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
27 account_row_data[ACCOUNT_PWD_COL], "退出"))
28 account_row_data[ACCOUNT_TEST_RESULT_COL] = "pass"
29 except:
30 error("登录失败【用户名:{}, 密码:{}, 断言关键字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
31 account_row_data[ACCOUNT_PWD_COL], "退出"))
32 account_row_data[ACCOUNT_TEST_RESULT_COL] = "fail"
33 account_row_data[ACCOUNT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
34 account_row_data[ACCOUNT_SCREENSHOT_COL] = take_screenshot(driver)
35 # 写入登录用例的测试结果
36 excel.change_sheet("测试结果")
37 excel.write_row_data(account_headline_data, "red")
38 excel.write_row_data(account_row_data)
39 excel.save()
40
41 # 切换另一个账号时需先关闭浏览器,否则会自动登录
42 driver.quit()
43
44
45 if __name__ == "__main__":
46 batch_login(TEST_DATA_FILE_PATH, "chrome", "126账号")
batch_login_and_add_contact_process.py
 1 from action.case_action import *
2 from util.excel_util import *
3 from conf.global_var import *
4 from util.datetime_util import *
5 from util.screenshot import take_screenshot
6
7
8 # 封装测试数据文件中用例的执行逻辑
9 # 测试数据文件中每个登录账号下,添加所有联系人数据
10 def batch_login_and_add_contact(test_data_file, browser_name, account_sheet_name):
11 excel = Excel(test_data_file)
12 # 获取登录账号sheet页数据
13 excel.change_sheet(account_sheet_name)
14 account_all_data = excel.get_all_row_data()
15 account_headline_data = account_all_data[0]
16 for account_row_data in account_all_data[1:]:
17 # 执行登录用例
18 account_row_data[ACCOUNT_TEST_TIME_COL] = get_english_datetime()
19 if account_row_data[ACCOUNT_IS_EXECUTE_COL].lower() == "n":
20 continue
21 # 初始化浏览器
22 driver = init_browser(browser_name)
23 # 获取联系人数据sheet
24 contact_data_sheet = account_row_data[ACCOUNT_DATA_SHEET_COL]
25 try:
26 # 默认以"退出"作为断言关键字
27 login(driver, account_row_data[ACCOUNT_USERNAME_COL], account_row_data[ACCOUNT_PWD_COL], "退出")
28 info("登录成功【用户名:{}, 密码:{}, 断言关键字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
29 account_row_data[ACCOUNT_PWD_COL], "退出"))
30 account_row_data[ACCOUNT_TEST_RESULT_COL] = "pass"
31 except:
32 error("登录失败【用户名:{}, 密码:{}, 断言关键字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
33 account_row_data[ACCOUNT_PWD_COL], "退出"))
34 account_row_data[ACCOUNT_TEST_RESULT_COL] = "fail"
35 account_row_data[ACCOUNT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
36 account_row_data[ACCOUNT_SCREENSHOT_COL] = take_screenshot(driver)
37 # 写入登录用例的测试结果
38 excel.change_sheet("测试结果")
39 excel.write_row_data(account_headline_data, "red")
40 excel.write_row_data(account_row_data)
41 excel.save()
42
43 # 执行添加联系人用例
44 excel.change_sheet(contact_data_sheet)
45 contact_all_data = excel.get_all_row_data()
46 contact_headline_data = contact_all_data[0]
47 # 在测试结果中,一个账号下的联系人数据标题行仅写一次
48 contact_headline_flag = True
49 for contact_row_data in contact_all_data[1:]:
50 if contact_row_data[CONTACT_IS_EXECUTE_COL].lower() == "n":
51 continue
52 contact_row_data[CONTACT_TEST_TIME_COL] = get_english_datetime()
53 try:
54 add_contact(driver, contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
55 contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
56 contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL])
57 info("添加联系人成功【姓名:{}, 邮箱:{}, 手机号:{}, 是否星标联系人:{}, "
58 "备注:{}, 断言关键字:{}】".format(contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
59 contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
60 contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL]))
61 contact_row_data[CONTACT_TEST_RESULT_COL] = "pass"
62 except:
63 error("添加联系人失败【姓名:{}, 邮箱:{}, 手机号:{}, 是否星标联系人:{}, "
64 "备注:{}, 断言关键字:{}】".format(contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
65 contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
66 contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL]))
67 contact_row_data[CONTACT_TEST_RESULT_COL] = "fail"
68 contact_row_data[CONTACT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
69 contact_row_data[CONTACT_SCREENSHOT_COL] = take_screenshot(driver)
70 # 写入登录用例的测试结果
71 excel.change_sheet("测试结果")
72 if contact_headline_flag:
73 excel.write_row_data(contact_headline_data, "red")
74 contact_headline_flag = False
75 excel.write_row_data(contact_row_data)
76 excel.save()
77
78 # 切换另一个账号时需先关闭浏览器,否则会自动登录
79 driver.quit()
80
81
82 if __name__ == "__main__":
83 batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126账号")
util 包

用于实现测试过程中调用的工具类方法,例如读取配置文件、页面元素的 *** 作方法、 *** 作Excel文件等。


excel_util.py

(openpyxl 版本:3.0.4)

  1 from openpyxl import load_workbook
2 from openpyxl.styles import PatternFill, Font, Side, Border
3 import os
4
5
6 class Excel:
7
8 def __init__(self, test_data_file_path):
9 # 文件格式校验
10 if not os.path.exists(test_data_file_path):
11 print("Excel工具类初始化失败:【{}】文件不存在!".format(test_data_file_path))
12 return
13 if not test_data_file_path.endswith(".xlsx") or not test_data_file_path.endswith(".xlsx"):
14 print("Excel工具类初始化失败:【{}】文件非excel文件类型!".format(test_data_file_path))
15 return
16 # 打开指定excel文件
17 self.wb = load_workbook(test_data_file_path)
18 # 初始化默认sheet
19 self.ws = self.wb.active
20 # 保存文件时使用的文件路径
21 self.test_data_file_path = test_data_file_path
22 # 初始化红、绿色,供样式使用
23 self.color_dict = {"red": "FFFF3030", "green": "FF008B00"}
24
25 # 查看所有sheet名称
26 def get_sheets(self):
27 return self.wb.sheetnames
28
29 # 根据sheet名称切换sheet
30 def change_sheet(self, sheet_name):
31 if sheet_name not in self.get_sheets():
32 print("sheet切换失败:【{}】指定sheet名称不存在!".format(sheet_name))
33 return
34 self.ws = self.wb.get_sheet_by_name(sheet_name)
35
36 # 返回当前sheet的最大行号
37 def max_row_num(self):
38 return self.ws.max_row
39
40 # 返回当前sheet的最大列号
41 def max_col_num(self):
42 return self.ws.max_column
43
44 # 获取指定行数据(设定索引从0开始)
45 def get_one_row_data(self, row_no):
46 if row_no < 0 or row_no > self.max_row_num()-1:
47 print("输入的行号【{}】有误:需在0至最大行数之间!".format(row_no))
48 return
49 # API的索引从1开始
50 return [cell.value for cell in self.ws[row_no+1]]
51
52 # 获取指定列数据
53 def get_one_col_data(self, col_no):
54 if col_no < 0 or col_no > self.max_col_num()-1:
55 print("输入的列号【{}】有误:需在0至最大列数之间!".format(col_no))
56 return
57 return [cell.value for cell in tuple(self.ws.columns)[col_no+1]]
58
59 # 获取当前sheet的所有行数据
60 def get_all_row_data(self):
61 result = []
62 # # API的索引从1开始
63 for row_data in self.ws[1:self.max_row_num()]:
64 result.append([cell.value if cell.value is not None else "" for cell in row_data])
65 return result
66
67 # 追加一行数据
68 def write_row_data(self, data, fill_color=None, font_color=None, border=True):
69 if not isinstance(data, (list, tuple)):
70 print("追加的数据类型有误:需为列号或元组类型!【{}】".format(data))
71 return
72 self.ws.append(data)
73 # 添加字体颜色
74 if font_color:
75 if font_color in self.color_dict.keys():
76 font_color = self.color_dict[font_color]
77 # 需要设置的单元格长度应与数据长度一致,否则默认与之前行的长度一致
78 count = 0
79 for cell in self.ws[self.max_row_num()]:
80 if count > len(data) - 1:
81 break
82 # cell不为None,才能设置样式
83 if cell:
84 if cell.value in ["pass", "成功"]:
85 cell.font = Font(color=self.color_dict["green"])
86 elif cell.value in ["fail", "失败"]:
87 cell.font = Font(color=self.color_dict["red"])
88 else:
89 cell.font = Font(color=font_color)
90 count += 1
91 # 添加背景颜色
92 if fill_color:
93 if fill_color in self.color_dict.keys():
94 fill_color = self.color_dict[fill_color]
95 count = 0
96 for cell in self.ws[self.max_row_num()]:
97 if count > len(data) - 1:
98 break
99 if cell:
100 cell.fill = PatternFill(fill_type="solid", fgColor=fill_color)
101 count += 1
102 # 添加单元格边框
103 if border:
104 bd = Side(style="thin", color="000000")
105 count = 0
106 for cell in self.ws[self.max_row_num()]:
107 if count > len(data) - 1:
108 break
109 if cell:
110 cell.border = Border(left=bd, right=bd, top=bd, bottom=bd)
111 count += 1
112
113 # 保存文件
114 def save(self):
115 self.wb.save(self.test_data_file_path)
116
117
118 if __name__ == "__main__":
119 from conf.global_var import *
120 excel = Excel(TEST_DATA_FILE_PATH)
121 excel.change_sheet("登录1")
122 # print(excel.get_all_row_data())
123 excel.write_row_data((1,2,"嘻哈",None,"ddd"), "red", "green")
124 excel.save()
find_element_util.py
 1 from selenium.webdriver.support.ui import WebDriverWait
2
3
4 # 显式等待一个对象
5 def find_element(driver, locate_method, locate_exp):
6 # 显式等待对象(最多等10秒,每0.2秒判断一次等待的条件)
7 return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_element(locate_method, locate_exp))
8
9
10 # 显式等待一组对象
11 def find_elements(driver, locate_method, locate_exp):
12 # 显式等待对象(最多等10秒,每0.2秒判断一次等待的条件)
13 return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_elements(locate_method, locate_exp))
ini_parser.py
 1 import configparser
2
3
4 class IniParser:
5
6 # 初始化打开指定ini文件并指定编码
7 def __init__(self, file_path, section):
8 self.cf = configparser.ConfigParser()
9 self.cf.read(file_path, encoding="utf-8")
10 self.section = section
11
12 # 获取所有分组名称
13 def get_sections(self):
14 return self.cf.sections()
15
16 # 获取指定分组的所有键
17 def get_options(self):
18 return self.cf.options(self.section)
19
20 # 获取指定分组的键值对
21 def get_items(self):
22 return self.cf.items(self.section)
23
24 # 获取指定分组的指定键的值
25 def get_value(self, key):
26 return self.cf.get(self.section, key)
datetime_util.py
 1 import time
2
3
4 # 返回中文格式的日期:xxxx年xx月xx日
5 def get_chinese_date():
6 year = time.localtime().tm_year
7 if len(str(year)) == 1:
8 year = "0" + str(year)
9 month = time.localtime().tm_mon
10 if len(str(month)) == 1:
11 month = "0" + str(month)
12 day = time.localtime().tm_mday
13 if len(str(day)) == 1:
14 day = "0" + str(day)
15 return "{}年{}月{}日".format(year, month, day)
16
17
18 # 返回英文格式的日期:xxxx/xx/xx
19 def get_english_date():
20 year = time.localtime().tm_year
21 if len(str(year)) == 1:
22 year = "0" + str(year)
23 month = time.localtime().tm_mon
24 if len(str(month)) == 1:
25 month = "0" + str(month)
26 day = time.localtime().tm_mday
27 if len(str(day)) == 1:
28 day = "0" + str(day)
29 return "{}/{}/{}".format(year, month, day)
30
31
32 # 返回中文格式的时间:xx时xx分xx秒
33 def get_chinese_time():
34 hour = time.localtime().tm_hour
35 if len(str(hour)) == 1:
36 hour = "0" + str(hour)
37 minute = time.localtime().tm_min
38 if len(str(minute)) == 1:
39 minute = "0" + str(minute)
40 second = time.localtime().tm_sec
41 if len(str(second)) == 1:
42 second = "0" + str(second)
43 return "{}时{}分{}秒".format(hour, minute, second)
44
45
46 # 返回英文格式的时间:xx:xx:xx
47 def get_english_time():
48 hour = time.localtime().tm_hour
49 if len(str(hour)) == 1:
50 hour = "0" + str(hour)
51 minute = time.localtime().tm_min
52 if len(str(minute)) == 1:
53 minute = "0" + str(minute)
54 second = time.localtime().tm_sec
55 if len(str(second)) == 1:
56 second = "0" + str(second)
57 return "{}:{}:{}".format(hour, minute, second)
58
59
60 # 返回中文格式的日期时间
61 def get_chinese_datetime():
62 return get_chinese_date() + " " + get_chinese_time()
63
64
65 # 返回英文格式的日期时间
66 def get_english_datetime():
67 return get_english_date() + " " + get_english_time()
68
69
70 if __name__ == "__main__":
71 print(get_chinese_datetime())
72 print(get_english_datetime())
log_util.py
 1 import logging
2 import logging.config
3 from conf.global_var import *
4
5
6 # 日志配置文件:多个logger,每个logger指定不同的handler
7 # handler:设定了日志输出行的格式
8 # 以及设定写日志到文件(是否回滚)?还是到屏幕
9 # 还定了打印日志的级别
10 logging.config.fileConfig(LOG_CONF_FILE_PATH)
11 logger = logging.getLogger("example01")
12
13
14 def debug(message):
15 logging.debug(message)
16
17
18 def info(message):
19 logging.info(message)
20
21
22 def warning(message):
23 logging.warning(message)
24
25
26 def error(message):
27 logging.error(message)
28
29
30 if __name__ == "__main__":
31 debug("hi")
32 info("gloryroad")
33 warning("hello")
34 error("这是一个error日志")
screenshot.py
 1 import traceback
2 import os
3 from util.datetime_util import *
4 from conf.global_var import *
5
6
7 # 截图函数
8 def take_screenshot(driver):
9 # 创建当前日期目录
10 dir = os.path.join(SCREENSHOT_PATH, get_chinese_date())
11 if not os.path.exists(dir):
12 os.makedirs(dir)
13 # 以当前时间为文件名
14 file_name = get_chinese_time()
15 file_path = os.path.join(dir, file_name+".png")
16 try:
17 driver.get_screenshot_as_file(file_path)
18 # 返回截图文件的绝对路径
19 return file_path
20 except:
21 print("截图发生异常【{}】".format(file_path))
22 traceback.print_exc()
23 return file_path
conf 包

配置文件及全局变量。


elements_repository.ini
[126mail_loginPage]
loginPage.frame=xpath>//iframe[contains(@id,'x-URS-iframe')]
loginPage.username=xpath>//input[@name='email']
loginPage.password=xpath>//input[@name='password']
loginPage.loginbutton=id>dologin [126mail_homePage]
homePage.addressLink=xpath>//div[text()='通讯录'] [126mail_contactPersonPage]
contactPersonPage.createButton=xpath>//span[text()='新建联系人']
contactPersonPage.name=xpath>//a[@title='编辑详细姓名']/preceding-sibling::div/input
contactPersonPage.email=xpath>//*[@id='iaddress_MAIL_wrap']//input
contactPersonPage.starContacts=xpath>//span[text()='设为星标联系人']/preceding-sibling::span/b
contactPersonPage.phone=xpath>//*[@id='iaddress_TEL_wrap']//dd//input
contactPersonPage.otherinfo=xpath>//textarea
contactPersonPage.confirmButton=xpath>//span[.='确 定']
global_var.py
 1 import os
2
3
4 # 工程根路径
5 PROJECT_ROOT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
6
7 # 元素定位方法的ini配置文件路径
8 ELEMENT_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "elements_repository.ini")
9
10 # 驱动路径
11 CHROME_DRIVER = "E:\\auto_test_driver\\chromedriver.exe"
12 IE_DRIVER = "E:\\auto_test_driver\\IEDriverServer.exe"
13 FIREFOX_DRIVER = "E:\\auto_test_driver\\geckodriver.exe"
14
15 # 测试使用的浏览器
16 BROWSER_NAME = "chrome"
17
18 # 登录主页
19 LOGIN_URL = "https://mail.126.com"
20
21 # 日志配置文件路径
22 LOG_CONF_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "logger.conf")
23
24 # 测试用例文件路径
25 TEST_DATA_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "test_data", "测试用例.xlsx")
26
27 # 截图保存路径
28 SCREENSHOT_PATH = os.path.join(PROJECT_ROOT_PATH, "screenshot_path")
29
30 # 单元测试报告输出目录
31 UNITTEST_REPORT_PATH = os.path.join(PROJECT_ROOT_PATH, "report")
32
33 # 登录账号sheet页数据列号
34 ACCOUNT_USERNAME_COL = 1
35 ACCOUNT_PWD_COL = 2
36 ACCOUNT_DATA_SHEET_COL = 3
37 ACCOUNT_IS_EXECUTE_COL = 4
38 ACCOUNT_TEST_TIME_COL = 5
39 ACCOUNT_TEST_RESULT_COL = 6
40 ACCOUNT_TEST_EXCEPTION_INFO_COL = 7
41 ACCOUNT_SCREENSHOT_COL = 8
42
43 # 联系人sheet页数据列号
44 CONTACT_NAME_COL = 1
45 CONTACT_EMAIL_COL = 2
46 CONTACT_IS_STAR_COL = 3
47 CONTACT_PHONE_COL = 4
48 CONTACT_REMARK_COL = 5
49 CONTACT_ASSERT_KEYWORD_COL = 6
50 CONTACT_IS_EXECUTE_COL = 7
51 CONTACT_TEST_TIME_COL = 8
52 CONTACT_TEST_RESULT_COL = 9
53 CONTACT_TEST_EXCEPTION_INFO_COL = 10
54 CONTACT_SCREENSHOT_COL = 11
55
56
57 if __name__ == "__main__":
58 print(PROJECT_ROOT_PATH)
logger.conf
###############################################
[loggers]
keys=root,example01,example02
[logger_root]
level=DEBUG
handlers=hand01,hand02 [logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0 [logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0 ###############################################
[handlers]
keys=hand01,hand02,hand03 [handler_hand01]
class=StreamHandler
level=INFO
formatter=form01
args=(sys.stderr,) [handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('.\\log\\126_mail_test.log', 'a') [handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form01
args=('.\\log\\126_mail_test.log', 'a', 10*1024*1024, 5) ###############################################
[formatters]
keys=form01,form02 [formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%Y-%m-%d %H:%M:%S [formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
test_data 目录

测试用例.xlsx:包含测试数据输入、测试结果输出

log 目录

日志输出文件:126_mail_test.log

...
...
2021-02-23 16:59:15 log_util.py[line:19] INFO 登录成功【用户名:zhangjun252950418, 密码:zhangjun123, 断言关键字:退出】
2021-02-23 16:59:20 log_util.py[line:19] INFO 添加联系人成功【姓名:lily, 邮箱:[email protected], 手机号:135xxxxxxx1, 是否星标联系人:是, 备注:常联系人, 断言关键字:[email protected]
2021-02-23 16:59:24 log_util.py[line:27] ERROR 添加联系人失败【姓名:张三, 邮箱:[email protected], 手机号:158xxxxxxx3, 是否星标联系人:否, 备注:不常联系人, 断言关键字:[email protected]
2021-02-23 16:59:27 log_util.py[line:19] INFO 添加联系人成功【姓名:李四, 邮箱:[email protected], 手机号:157xxxxxx9, 是否星标联系人:否, 备注:, 断言关键字:李四】
...
...
screenshot_path 目录

异常截图保存目录:

main.py

本 PO 框架的运行主入口。


 1 from business_process.batch_login import *
2 from business_process.batch_login_and_add_contact import *
3 from conf.global_var import *
4
5
6 # 示例组装:冒烟测试
7 def smoke_test():
8 batch_login(TEST_DATA_FILE_PATH, "chrome", "126账号")
9
10
11 # 示例组装:全量测试
12 def full_test():
13 batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126账号")
14
15
16 if __name__ == "__main__":
17 # smoke_test()
18 full_test()

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/586373.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存