Page Object Model:
PO设计模式是selenium自动化测试中最佳的设计模式之一,主要体现在对界面交互细节的封装,也就是在实际测试中只关注业务流程就OK了
传统的设计中,在新增测试用例之后,代码会有以下几个问题:
1.易读性差:一连串的find element会使代码显得杂乱无章
2.可扩展性不好:用例孤立,无法扩展
3.可复用性差:无公共方法,很难复用
4.可维护性差:一旦元素变化,需要维护修改大量测试用例
因此考虑到优化:
PO模式是一种自动化测试设计模式,讲页面定位和业务操作分开,也就是把对象的定位和测试脚本分开,从而提供可维护性。
首先抽象封装一个BasePage类,这个基类拥有一些指向Webdriver实例的属性,然后每一个Page继承基类BasePage,可以通过driver管理每一个Page中的元素,而且在Page中将这些操作封装为一个一个的方法。TestCase继承unittest里面的TestCase类,并且依赖page类,进行组织测试步骤的工作。
这样做的好处,就是有元素变化,只需要维护每一个Page就行了,测试步骤变化,只需要维护TestCase即可
PO各个核心要素的介绍:
BasePage:
class BasePage(object): def __init__(self,driver): self.driver = driver pass
Page:
from SeleniumProject.PO.BasePage import BasePage class LoginBase(BasePage): # 定位元素,括号中是通过find_element来获取元素的属性 uname = () pwd = () def set_uname(self,uname): name =self.driver.find_element(*LoginBase.uname) name.send_keys("用户名") def set_pwd(self,pwd): password = self.driver.find_element(*LoginBase.pwd) password.send_keys("密码") pass
TestCase:
from unittest import TestCase import unittest from selenium import webdriver class Test_Login(TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.get("https://cn.bing.com/") # 测试步骤 def test_Login(self): self.driver.get(self.base_url) pass def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()
下面举一个简单的例子来看一下PO模式:
业务要求就是,用Chrome浏览器,在https://cn.bing.com/中搜索“墨菲特”,然后点击搜索按钮,再关闭浏览器
基类BasePage类:
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage(object): """BasePage封装所有页面的公有方法,例如url、driver、find_element""" # 构造函数里面的参数就是类的所有参数 def __init__(self,selenuime_driver,base_url): self.driver = selenuime_driver self.url = base_url # 定义一个私有方法,其他类不能调用该方法 def _open(self,url): self.driver.get(url) self.driver.maximize_window() # 定义open()方法,调用_open()方法 def open(self): self._open(self.base_url) # 重写find_element()方法,参数为任意数量的(带*的参数) # 此方法是为了保证元素是可见的 def find_emelemt(self,*loc): try: # 保证元素可见 WebDriverWait(self.driver,10).until(EC.visibility_of_all_elements_located(loc)) return self.driver.find_element(*loc) except: print("页面中没有%s %" % (self.loc)) # 定义script()方法,用于执行JS脚本,比方上上传文件啥的 def script(self,src): self.driver.excute_script(src) # 定义页面跳转方法,比方说有的页面有frame嵌套 def switch_frame(self,loc): return self.driver.switch_to_frame(loc) # 重新定义send_keys()方法,为了保证搜索按钮是否存在,还有有的输入框中默认有值,要清空 def send_keys(self,loc,value,clear_first=True,click_first=True): try: # getattr方法相当于实现了self.loc loc = getattr(self,"_%s"%loc) # 是否存在搜索按钮 if click_first: self.find_element(*loc).click() # 清空搜索框中的值,并输入需要搜索的值 if clear_first: self.find_emelemt(*loc).clear() self.find_emelemt(*loc).send_keys(value) except: print("页面上未找到%s元素"%(self.loc))
Page类:
from selenium.webdriver.common.by import By from SeleniumProject.PO.BasePage import BasePage class SearchPage(BasePage): # 定位元素 search_loc = (By.NAME,"q") #搜索框 btn_loc = (By.NAME,"go") #搜索按钮 # 重写父类的open()方法 def open(self): self._open(self.base_url) def search_content(self,content): # 调用父类的find_emelemt,然后将本类的参数传入 content1 = self.find_emelemt(*self.search_loc) content1.send_keys(content) def btn_click(self): btn1 = self.find_emelemt(*self.btn_loc) btn1.click()
TestCase类:
from unittest import TestCase import unittest from selenium import webdriver from time import sleep from SeleniumProject.PO.Search import SearchPage class CaseRun(TestCase): def setUp(self): self.driver = webdriver.Chrome() sleep(3) self.url = "https://cn.bing.com/" sleep(3) self.content = "墨菲特" # 测试步骤 def test_search(self): bing_page = SearchPage(self.driver,self.url) bing_page.open() sleep(3) bing_page.search_content(self.content) sleep(3) bing_page.btn_click() sleep(3) def tearDown(self): self.driver.quit() if __name__ == "__main__": unittest.main()