-
PO设计原理:
将页面封装成对象
页面对象内封装业务方法
元素定位方法可存放到其他配置文件
PO.png
PO设计是一种思想,任何UI自动化测试都可以套用这种思想,不仅限于selenium的webUI模式
PO设计规范
-
页面对象化
1.每个页面封装成对象
例如:登录页面可以设计成LoginPage类
2.封装具体业务方法
例如:登陆页面的登陆方法有 login(username,password)
3.不保存具体的元素定位
例如:定位用户名和密码框的表达式不写在代码里,放在外部配置文件当中 -
页面元素属性化
1.只涉及到要操作的元素名称
2.具体的定位方式方法不写在代码里 -
元素定位可配置化
1.配置以键值对形式存在
2.区分出不同的页面
3.保存元素配置的文件形式没有限制文本文件、excel、数据库等
页面类的设计
-
页面类的层级抽象
1.构造页面对象
创建页面基类,封装基本操作方法,由其他业务类继承
2.封装相关业务方法
业务页面对象中封装常用的业务操作
3.抽取元素定位
抽取具体的元素定位方法存放到配置文件
4.测试案例构成
结合页面对象编写具体的业务流 -
Yaml格式配置文件特点
1.YAML 的数据组织主要依赖的是空白,缩进,分行等结构,可读性好。
2.YAML 实现简单,解析成本很低,和脚本语言的交互性好。
3.YAML 使用实现语言的数据类型
4.YAML 表达能力强,扩展性好
5.安装方法 --pip install pyyaml
书写PO模式建议:
1.先写出具体的业务逻辑--比如登录
2.再将写好的逻辑封装到类里面
3.继续构造其他的页面类
4.将这些类共同的功能抽象化父类,被其他业务类继承
5.可以将代码内部的具体元素抽离出来用外部的配置文件进行管理
- base类:封装元素定位方法
from selenium.webdriver.support.select import Select
class BasePage:
def __init__(self,driver):
# 这里driver要传进来,如果不传进来直接用webdriver赋值,有多个页面继承base类会导致打开多个页面,使用的不是同一个浏览器对象
self.driver = driver
self.driver.implicitly_wait(10)
def click_element(self, locator):
self.driver.find_element(locator[0], locator[1]).click()
def input_text(self, locator, text):
self.driver.find_element(locator[0], locator[1]).clear()
self.driver.find_element(locator[0], locator[1]).send_keys(text)
def find_elements(self, locator):
# 如果不return返回的是空
return self.driver.find_elements(locator[0], locator[1])
def find_element(self,locator):
return self.driver.find_element(locator[0],locator[1])
def find_select(self,locator,text):
ele = Select(self.find_element(locator))
ele.select_by_visible_text(text)
- 页面类:封装各个页面
from selenium import webdriver
import time
from basepage import BasePage
from conf_util import get_locators
class LoginPage(BasePage):
def __init__(self, driver):
BasePage.__init__(self, driver)
self.driver.get('http://localhost/mgr/login/login.html')
LoginPage = get_locators('locator.yml')['LoginPage']
self.username_input = LoginPage['username']
self.password_input = LoginPage['password']
self.login_btn = LoginPage['login_btn']
def login(self, username, password):
self.input_text(self.username_input, username)
self.input_text(self.password_input, password)
self.click_element(self.login_btn)
# 返回下一个流程的页面
return CoursePage(self.driver)
class CoursePage(BasePage):
def __init__(self, driver):
BasePage.__init__(self, driver)
CoursePage = get_locators('locator.yml')['Coursepage']
self.addCourseBtn = CoursePage['addCourseBtn']
self.courseName = CoursePage['courseName']
self.courseDesc = CoursePage['courseDesc']
self.courseIdx = CoursePage['courseIdx']
self.createBtn = CoursePage['createBtn']
self.confirmBtn = CoursePage['confirmBtn']
self.delBtns = CoursePage['delBtns']
self.courseBtn = CoursePage['courseBtn']
def add_Course(self, name, dec, idx):
self.click_element(self.courseBtn)
# 点击添加课程
self.click_element(self.addCourseBtn)
# 输入课程名称
self.input_text(self.courseName, name)
# 输入课程描述
self.input_text(self.courseDesc, dec)
# 输入展示次序
self.input_text(self.courseIdx, idx)
# 点击创建
self.click_element(self.createBtn)
def delete_allCourse(self):
self.click_element(self.courseBtn)
self.driver.implicitly_wait(1)
while True:
delBtns = self.find_elements(self.delBtns)
if not delBtns: # 课程全部删除,删除按钮没有了
break
# 删除课程的具体过程
# time.sleep(1)
delBtns[0].click() # 每次删除第一个课程
time.sleep(1)
self.click_element(self.confirmBtn)
time.sleep(1) # 给弹出框消失的时间,防止点击下面的删除按钮点不到
self.driver.implicitly_wait(10)
# 此方法防止如果不是从登录界面进入课程页面,好比从课时页面到课程页面
def get_coursePage(self):
# 确保浏览器访问页面
self.driver.get('http://localhost/mgr/ps/mgr/index.html#/')
# 返回这个页面对象
return self
def driverquit(self):
self.driver.quit()
class TeacherPage(BasePage):
def __init__(self, driver):
BasePage.__init__(self, driver)
TeacherPage = get_locators('locator.yml')['TeacherPage']
self.clickToAdd = TeacherPage['clickToAdd']
self.actualName = TeacherPage['actualName']
self.loginName = TeacherPage['loginName']
self.desc = TeacherPage['desc']
self.idx = TeacherPage['idx']
self.selectCourse = TeacherPage['selectCourse']
#老师页面
self.teacherMenu = TeacherPage['teacherMenu']
self.clickAddCourse = TeacherPage['clickAddCourse']
self.determine = TeacherPage['determine']
self.check = TeacherPage['check']
self.delectTeacher = TeacherPage['delectTeacher']
#删除确定
self.sureToDelete = TeacherPage['sureToDelete']
def add_teacher(self,actualName,loginName,desc,idx,course):
self.click_element(self.teacherMenu)
time.sleep(3)
# 点击添加老师
self.click_element(self.clickToAdd)
# 输入老师姓名
self.input_text(self.actualName,actualName)
# 输入登录名
self.input_text(self.loginName,loginName)
# 输入描述
self.input_text(self.desc,desc)
# 输入次序
self.input_text(self.idx,idx)
# 选择授课信息
self.find_select(self.selectCourse,course)
# 点击加号
self.click_element(self.clickAddCourse)
#点击确定
self.click_element(self.determine)
# 校验老师是否添加成功
assert actualName in [name.text for name in self.find_elements(self.check)], '老师添加失败'
# 删除所有老师用于初始化环境
def delete_teacher(self):
self.click_element(self.teacherMenu)
self.driver.implicitly_wait(1)
while True:
delBtns = self.find_elements(self.delectTeacher)
if not delBtns: # 课程全部删除,删除按钮没有了
break
# 删除课程的具体过程
# time.sleep(1)
delBtns[0].click() # 每次删除第一个课程
time.sleep(1)
self.click_element(self.sureToDelete)
time.sleep(1) # 给弹出框消失的时间,防止点击下面的删除按钮点不到
self.driver.implicitly_wait(10)
-Yaml 文件存储个页面的具体定位方式
LoginPage:
username: ['id', 'username']
password: ['id', 'password']
login_btn: ['css selector', '[class="btn btn-success"]']
Coursepage:
addCourseBtn: ['css selector', '[ng-click="showAddOne=true"]']
courseName: ['css selector', '[ng-model="addData.name"]']
courseDesc: ['css selector', '[ng-model="addData.desc"]']
courseIdx: ['css selector', '[ng-model="addData.display_idx"]']
createBtn: ['css selector', '[ng-click="addOne()"]']
confirmBtn: ['css selector', '.btn-primary']
delBtns: ['css selector', '[ng-click="delOne(one)"]']
courseBtn: ['css selector','a[ui-sref="course"]']
TeacherPage:
clickToAdd: ['css selector','[ng-click="showAddOne=true"]']
actualName: ['css selector','input[ng-model="addEditData.realname"]']
loginName: ['css selector','input[ng-model="addEditData.username"]']
desc: ['css selector','textarea[ng-model="addEditData.desc"]']
idx: ['css selector','input[ng-model="addEditData.display_idx"]']
selectCourse: ['css selector','select[ng-model="$parent.courseSelected"]']
teacherMenu: ['css selector','a[ui-sref="teacher"]']
clickAddCourse: ['css selector','i[class="fa fa-plus"]']
determine: ['css selector','[ng-click="addOne()"]']
check: ['css selector','tr.ng-scope td:nth-of-type(2)']
delectTeacher: ['css selector','button[ng-click="delOne(one)"]']
sureToDelete: ['css selector','[class="btn btn-primary"]']
- 此文件用于读取yaml文件并加载
import yaml
yaml.warnings({'YAMLLoadWarning':False})
def get_locators(yamlfile):
with open(yamlfile, 'r') as f:
f = f.read()
#载入读取文本
res = yaml.load(f)
return res
- unittest 添加测试项
import unittest
from selenium import webdriver
from page import TeacherPage,LoginPage
class webtest(unittest.TestCase):
def setUp(self):
driver = webdriver.Chrome()
self.lp = LoginPage(driver)
self.tp = TeacherPage(driver)
self.cp = self.lp.login('xxoo', 'xxoo')
self.cp.delete_allCourse()
self.cp.add_Course('初中化学', '课程描述', 2)
self.tp.delete_teacher()
def test(self):
self.tp.add_teacher('隔壁老王', '嘿咻', '嘿嘿咻咻', 3, '活塞运动学')
def tearDown(self):
self.cp.delete_allCourse()
self.cp.driverquit()
if __name__ == '__main__':
unittest.main()
网友评论