目录
driver.py:
-
编写存放主流浏览器驱动的方法,返回值为浏览器的驱动
from selenium import webdriver
'''
存放驱动
'''
def my_Edge():
driver = webdriver.Edge()
return driver
BasePage.py:
-
二次封装myselenium类
导入写好的driver方法
-
在类中初始化(init)驱动和url(驱动为自己编写的driver方法返回的驱动,url为具体地址)
-
编写打开网址方法(my_get)
-
编写定位元素方法(my_find_element),传参一个列表(*代表数据类型为列表)
-
编写弹窗处理方法(my_alert),传参一个布尔值,ture时确定弹窗,flase时取消弹窗
'''封装selenium的基础操作类方法(get、find_element等)'''
class basepage:
def __init__(self, driver):
self.url = 'http://192.168.5.10/pams/'
self.driver = driver
def my_get(self):
self.driver.get(self.url)
def my_find_element(self, *loc):
return self.driver.find_element(*loc)
def my_alert(self, istrue: bool):
new_driver = self.driver.switch_to.alert()
if istrue:
new_driver.accept()
else:
new_driver.dissmiss()
function.py:
导入os(python与系统之间路径的相关操作)\time(后续用当前时间与截图文件名做拼接)\pyautogui(python内置的截图)\csv(数据读取)
-
当页面有弹窗拦截时,我们无法通过驱动(driver.get_screenshort_as_file())进行截图,所以就用到pyautogui模块,该模块是跳过驱动,直接用后台截图的,可以捕捉到有弹窗的页面。需下载。
-
测试过程中极可能会对某个模块进行多轮测试,模块名相同的情况下,不利于我们观察测试结果,所以我们可以利用time模块,获取当前时间,并于模块名进行拼接,形成较易区分的全新截图名称。
-
为提高代码的复用性及准确性(避免因为路径问题而测试失败),我们可以利用os模块获取绝对路径
截图:
-
新建截图方法,传参模块名称、驱动、当前页面是否有弹窗
-
获取当前时间(time.strftime('%Y-%m-%d-%H%M%S', time.localtime()))
-
拼接模块名称、时间和文件后缀名(.png),形成截图名称
-
利用os.path.dirname()层层获取根目录,再利用os.path.join()拼接存放截图的全路径
-
将截图名称join到存放截图的全路径,调用get_screenshot_as_file(),并传入全新路径
数据读取:
-
可加入是否忽略首行选项
import os
import csv
import time
'''
截图、数据驱动读取、获取弹窗文本等方法
'''
def my_csv(filename, isIgonre: bool):
'''
读取文件内容,生成列表
:param path: 数据文件路径
:param isIgonre: 是否忽略首行
:return: 返回值为全新列表
'''
# 获取当前文件的根目录,__file__ 当前文件
model = os.path.dirname(__file__)
test_case = os.path.dirname(model)
web_site = os.path.dirname(test_case)
# 将传入的文件名与存放该文件的根目录进行拼接,生成该文件的全路径
new_path = os.path.join(web_site, 'test_data', filename)
with open(new_path, mode='r', encoding='utf-8') as f1:
f1_data = csv.reader(f1)
result_list = []
index = 0
for i in f1_data:
if isIgonre:
if index != 0:
result_list.append(i)
else:
result_list.append(i)
index += 1
return result_list
def my_screenshot(driver, photo_name):
'''
截图方法
:param driver: 驱动
:param photo_name:截图名称
:return: 无
'''
# 拼接时间,localtime()获取当前时间戳
filetime = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
file_name = photo_name + '|' + filetime + '.png'
# 再获取路径、拼接路径,生成截图的存放路径
modle_path = os.path.dirname(__file__)
test_case_path = os.path.dirname(modle_path)
web_site_path = os.path.dirname(test_case_path)
all_file_path = os.path.join(web_site_path, "test_report", r"screenshot", file_name)
print(all_file_path)
driver.get_screenshot_as_file(all_file_path)
def get_alert_text(driver):
# 获取弹窗文本
real_text = driver.switch_to.alert.text
return real_text
myunit.py:
-
导入driver文件夹中的driver.py 和 unittest
-
封装一个继承自unittest.TestCase的类
-
编写setUp()方法,调用自己编写的浏览器驱动,初始化实际参与测试的驱动,增加智能等待和窗口最大化
-
编写tearDown()方法,关闭浏览器
import time
import unittest
from driver.driver import my_Edge
'''二次封装unittest框架中的Setup(添加5秒的智能等待和浏览器窗口最大化)和tearDown(退出浏览器)等方法'''
class my_unit(unittest.TestCase):
def setUp(self) -> None:
self.driver = my_Edge()
self.driver.implicitly_wait(5)
self.driver.maximize_window()
def tearDown(self) -> None:
time.sleep(1)
self.driver.quit()
AddPage.py:
-
封装继承自myselenium的类]
【步骤】
导入By和BasePage
-
定义类中的公共变量(该变量为页面上的元素及其定位方法),变量数据类型为元组,例:(By.ID,"")
-
定义定位元素及对元素的操作(输入、点击、清空等)方法,需传参输入的值(无输入即无参数),注意:因传参数据类型为元组,所以需在调用公共变量之前加入解包符号 *
add_action方法:
-
参数为:AddPage类型的实例化对象,品牌名称,品牌编码
-
方法具体内容:将AddPage类中封装的每一步操作连贯起来,使其成为一个完整的行为,即:通过该对象按照实际操作流程调用类中的每一个方法
from selenium.webdriver.common.by import By
from Website.test_case.page_object.BasePage import basepage
'''引入BasePage.py中封装好的方法,引入By方法类,
封装好商品品牌添加页面元素(使用LINK_TEXT方法封装商品品牌按钮,
使用XPATH方法封装新增按钮,
使用XPATH方法封装商品品牌名称输入框,
使用CSS方法封装保存按钮,
封装点击商品品牌按钮、点击新增按钮、输入商品品牌名称、点击保存按钮等操作),
封装添加成功以及添加失败的提示信息文字'''
class add_page(basepage):
brand_btn_text = (By.LINK_TEXT, '品牌')
brand_new_xpath = (By.XPATH, '//*[@id="content"]/div[2]/div/div[1]/button')
brand_name_xpath = (By.XPATH, '//*[@id="title"]')
brand_code_xpath = (By.XPATH, '//*[@id="code"]')
brand_save_css = (By.CSS_SELECTOR, '.btn.blue.margin-right-10')
def brand_click(self):
self.my_find_element(*self.brand_btn_text).click()
def brand_new(self):
self.my_find_element(*self.brand_new_xpath).click()
def brand_name_input(self, brand_name):
self.my_find_element(*self.brand_name_xpath).send_keys(brand_name)
def brand_code(self, code):
self.my_find_element(*self.brand_code_xpath).send_keys(code)
def brand_save(self):
self.my_find_element(*self.brand_save_css).click()
def add_action(brand:add_page,brand_name, brand_code):
brand.brand_click()
brand.brand_new()
brand.brand_name_input(brand_name)
brand.brand_code(brand_code)
brand.brand_save()
LoginPage.py:
-
同理与AddPage.py
from time import sleep
from selenium.webdriver.common.by import By
from Website.test_case.page_object.BasePage import basepage
from driver.driver import my_Edge
from Website.test_case.model.function import my_csv
'''引入BasePage.py中封装好的方法,
引入By方法类,封装登录用例页面元素位置和操作
(使用ID方法封装用户名输入框,
使用NAME方法封装密码输入框、
使用CLASS方法封装登录按钮位置,
封装输入用户名、密码、点击登录按钮等操作)'''
class Login_page(basepage):
username_id = (By.ID, 'loginName')
password_name = (By.NAME, 'password')
login_btn_class = (By.CLASS_NAME, 'blue-button')
def username_input(self, username):
self.my_find_element(*self.username_id).send_keys(username)
def password_input(self, password):
self.my_find_element(*self.password_name).send_keys(password)
def login_btn_click(self):
self.my_find_element(*self.login_btn_class).click()
def login_action(login_ac: Login_page, username, password):
login_ac.my_get()
sleep(0.5)
login_ac.username_input(username)
login_ac.password_input(password)
sleep(0.5)
login_ac.login_btn_click()
if __name__ == '__main__':
login_ac = Login_page(my_Edge())
login_list = my_csv('login.csv', False)
print(login_list)
login_action(login_ac, login_list[0][0], login_list[0][1])
【注意】:在PO模式中调用二次封装的selenium中的find_element()方法时,需要在传的元组参数前加解包符号 *
test_csv.csv:
-
数据文件,存放测试过程中会用到的测试数据,如用户名、密码、断言所用到的预期提示文本等
-
数据与数据之间使用英文逗号间隔,同组数据存放在同一行
-
student,student,测试数据,,请填写品牌编码! student,student,123,,品牌名称只能包含中文字符! student,student,,,请填写品牌名称! student,student,测试数据,,请填写品牌编码! student,student,测,,请填写品牌编码!
test_add.py:
-
整合LoginPage中的登录行为和AddPage中的添加品牌行为,使它们成为一个具体的测试行为
-
业务流程:登录-->添加品牌-->断言页面返回的提示信息
【步骤】
导包:ddt / 自定义的function、myunit、loginpage、basepage、addpage
-
ddt有两种导包形式
-
import ddt
-
from ddt import ddt,data,unpack
-
使用第二种形式,后续装饰器可直接写@ddt/data/unpack
-
-
封装一个被@ddt.ddt装饰的类,该类继承自二次封装的myunit
-
在使用数据驱动的测试用例方法前使用@ddt.data()指明数据来源。
-
参数为:调用function中编写的csv数据读取方法,返回的全新数据列表。注:调用该方法需在前加列表标识符 *
-
-
在@ddt.ddt()方法后,换行使用@ddt.unpack解包
-
编写test开头的使用数据驱动的方法(测试用例),该方法须接收与数据文件内相同个数的参数,即数据文件有3个参数,该测试方法须有三个与之一一对应的参数
-
在测试方法内,创建LoginPage类和AddPage类的实例化对象,调用LoginPage.py中的登录行为的方法,传参该类的对象及解包后的参数,AddPage类同理。
-
进行断言
import ddt
from Website.test_case.model.function import *
from Website.test_case.model.myunit import my_unit
from page_object.LoginPage import *
from page_object.AddPage import *
'''引入unittest、ddt以及之前封装好的model,LoginPage,AddPage中的方法类,
根据用例编写6条测试用例脚本,创建data参数来接收测试数据,
并使用数据驱动输入用户名和密码(XTGLY/123456),
使用数据驱动输入商品品牌名称,然后进行截图操作,
最后对每一条测试用例进行assertIn断言操作,对比提示信息是否和预期一致;'''
@ddt.ddt
class add(my_unit):
@ddt.data(*my_csv('test_csv.csv', False))
@ddt.unpack
def test_add_brand(self, username, password, name, code, expect_text):
login = Login_page(self.driver)
brand = add_page(self.driver)
login_action(login, username, password)
add_action(brand, name, code)
self.assertIn(get_alert_text(self.driver), expect_text, '断言失败')
test_run.py:
-
执行测试文件,运行指定开头和格式的文件,并在指定位置生成报告(.html文件)
导包:os / unittest / HTMLTestRunner
-
创建测试集合
-
创建测试加载器
-
将指定路径下的指定格式的文件添加到测试集合
-
生成测试报告的指定存放路径
-
将结果写入测试报告
-
运行该测试集合
import os
import unittest
from HTMLTestRunner import HTMLTestRunner
suit = unittest.TestSuite()
loader = unittest.TestLoader()
suit.addTest(loader.discover('./test_case/', 'test*.py'))
path = os.path.join('./test_report/', 'test.html')
with open(path, mode='wb') as f1:
runner = HTMLTestRunner(f1, title='资产管理系统', description='添加品牌')
runner.run(suit)