1、什么是PO设计模式 (Page Object Model)
一种在测试自动化中变得流行的设计模式,使得自动化测试脚本的减少代码重复、更易读、减少维护成本。
一般PO设计模式有三层
第一层:
- 对Selenium 进行二次封装,定义一个所有页面都继承的 BasePage ,
- 封装 Selenium 基本方法 例如:元素定位,元素等待,导航页面 ,
- 不需要全部封装,用到多少方法就封装多少方法。
第二层:
- 页面元素进行分离,每个元素只定位一次,隔离定位,如果页面改变,只需要改变相应的元素定位;
- 业务逻辑分离 或 操作元素动作分离
第三层:
- 使用单元测试框架对业务逻辑进行测试
2、为什么要使用PO设计模式
- 页面频繁变化,(页面html结构等变化)导致页面UI元素频繁变动,元素定位改变
- 传统线性自动化(面向过程开发),用例中需要反复的定位同一个元素
- 每当页面发生变化的时候,需要在用例中寻找变动的部分,工作量大,容易产生遗漏,不容易维护
3、使用PO设计模式要点
- 不要在page页面对象外做元素定位
- 不在page页面对象里面写断言,除非是页面是否成功加载断言
- 需要多少个元素就定位多少个,不需要对整个页面的元素进行定位
- 当你的用例设计页面跳转时,例如登陆操作,登陆完成后跳转首页,当页面发生“跳转”
,封装的业务逻辑需要返回(return)对应的页面对象的实例 - BasePage封装Selenium 基础方法,不需要全部封装,用到多少方法就封装多少方法
4、 PO设计模式实例
实例登录Ecshop并选中商品编辑
- base封装底层元素
- page对页面元素定位
- report生成测试报告
- testcase编写测试用例
- main.py执行
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.select import Select class Base: def __init__(self, driver): self.driver = driver self.driver.maximize_window() def go_page(self, url): self.driver.get(url) def find_element(self, *loc): return self.driver.find_element(*loc) def find_elements(self,*loc): return self.driver.find_elements(*loc) def send_key(self, text, *loc): self.find_element(*loc).send_keys(text) def ele_clear(self, *loc): self.find_element(*loc).clear() def ele_click(self, *loc): self.find_element(*loc).click() def eles_click(self,num,*loc): self.driver.find_elements(*loc)[num].click() def page_title(self): return self.driver.title def web_quit(self): self.driver.quit() def wait_title_content(self, title): WebDriverWait(self.driver, 5).until(EC.title_contains(title)) def enter_frame(self,*loc): self.driver.switch_to.frame(*loc) def quit_frame(self): self.driver.switch_to.default_content() def select_index(self,index,*loc): Select(self.driver.find_element(*loc)).select_by_index(index) def select_value(self,value,*loc): Select(self.driver.find_element(*loc)).select_by_value(value) def select_text(self,text,*loc): Select(self.driver.find_element(*loc)).select_by_visible_text(text)
1 from selenium.webdriver.common.by import By 2 3 from Ecshop.base.base_element import Base 4 from selenium import webdriver 5 6 class LoginPage(Base): 7 page_url = 'http://41.hdy1314.com/admin/privilege.php?act=login' 8 login_username = (By.NAME, 'username') 9 login_password = (By.NAME, 'password') 10 login_submit = (By.CLASS_NAME, 'btn-a') 11 menu_frameid = (By.ID,'menu-frame') 12 menu_id02 = (By.XPATH, '//*[@id="menu-ul"]/li[2]') 13 main_frameid = (By.ID,'main-frame') 14 cat_id = (By.NAME,'cat_id') 15 submit_btn01 = (By.XPATH, '/html/body/div[9]/form/button') 16 #redact = (By.XPATH,'//*[@id="listDiv"]/table[1]/tbody/tr[3]/td[13]/a[2]') 17 redact = (By.LINK_TEXT, '编辑') 18 19 20 21 def __init__(self): 22 driver = webdriver.Chrome() 23 Base.__init__(self, driver) 24 self.go_page(self.page_url) 25 26 def send_username(self,text): 27 self.send_key(text, *self.login_username) 28 29 def send_password(self,text): 30 self.send_key(text, *self.login_password) 31 32 def submit(self): 33 self.ele_click(*self.login_submit) 34 35 def menu_frame(self): 36 return self.find_element(*self.menu_frameid) 37 38 def menu(self): 39 self.ele_click(*self.menu_id02) 40 41 def main_frame(self): 42 return self.find_element(*self.main_frameid) 43 44 def select_cat(self,num): 45 self.select_index(num,*self.cat_id) 46 47 def submit_btn(self): 48 self.ele_click(*self.submit_btn01) 49 50 def click_redact(self,num): 51 if len(self.find_elements(*self.redact)) > num: 52 self.eles_click(num,*self.redact) 53 else: 54 55 print("元素组的长度小于%s" %(num + 1))
1 import time 2 from Ecshop.page.login_page import LoginPage 3 import allure 4 5 @allure.feature('ECSHOP') 6 class TestSearch: 7 8 def setup_class(self): 9 10 self.ecshop_login = LoginPage() 11 12 @allure.story('登录Ecshop') 13 @allure.description('登录账号进入管理后台') 14 @allure.title("ECSHOP管理中心") 15 def test_case3(self): 16 self.ecshop_login.send_username('admin') 17 self.ecshop_login.send_password('admin123') 18 self.ecshop_login.submit() 19 #self.ecshop_login.wait_title_content('admin') 20 assert self.ecshop_login.page_title() == 'ECSHOP 管理中心' 21 tt = self.ecshop_login.menu_frame() 22 self.ecshop_login.enter_frame(tt) 23 self.ecshop_login.menu() 24 self.ecshop_login.quit_frame() 25 xf =self.ecshop_login.main_frame() 26 self.ecshop_login.enter_frame(xf) 27 self.ecshop_login.select_cat(5) 28 self.ecshop_login.submit_btn() 29 time.sleep(3) 30 self.ecshop_login.click_redact(1) 31 32 def teardown_class(self): 33 time.sleep(5) 34 self.ecshop_login.web_quit()
import os import pytest if __name__ == '__main__': pytest.main(['-vs', "./testcase/test_search.py", "--alluredir", "./report/allure-result"]) os.system(r"allure generate --clean ./report/allure-result -o ./report/allure-report")