Python+Selenium Page Automation Testing Using Page Object

  • 2021-07-16 02:49:48
  • OfStack

Page Object pattern is a test design pattern in Selenium, Mainly, every page is designed as an Class, which contains the elements (buttons, input boxes, titles, etc.) that need to be tested in the page, so that the page elements can be obtained by calling the page class in the Selenium test page, which skillfully avoids the need to change the test page code when the page element id or its position changes. When the page element id changes, you only need to change the properties of the page in the test page Class.

Page Object pattern is an automated test design pattern, which separates page positioning from business operations, separates test objects (element objects) from test scripts (use case scripts), and improves the maintainability of use cases.

unittest is a unit test framework, which is used to design various test cases. Page classes (objects) designed by PageObject can be called to design more maintainable use cases. It provides use case organization and execution, provides rich comparison (assertion) methods, and provides rich logs, which is suitable for web automated use case development and execution.

The design idea using PO mode is as follows:

1. Define the page base class and encapsulate the methods common to all pages.

Named test_8_3_2_BasePage. py


# coding=utf-8
'''
Created on 2016-8-13
@author: Jennifer
Project:基础类BasePage,封装所有页面都公用的方法,
定义open函数,重定义find_element,switch_frame,send_keys等函数。
在初始化方法中定义驱动driver,基本url,title
WebDriverWait提供了显式等待方式。
'''
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class BasePage(object):
  """
  BasePage封装所有页面都公用的方法,例如driver, url ,FindElement等
  """
  #初始化driver、url、pagetitle等
  #实例化BasePage类时,最先执行的就是__init__方法,该方法的入参,其实就是BasePage类的入参。
  #__init__方法不能有返回值,只能返回None
  #self只实例本身,相较于类Page而言。
  def __init__(self, selenium_driver, base_url, pagetitle):
    self.driver = selenium_driver
    self.base_url = base_url
    self.pagetitle = pagetitle
     
  #通过title断言进入的页面是否正确。
  #使用title获取当前窗口title,检查输入的title是否在当前title中,返回比较结果(True 或 False)
  def on_page(self, pagetitle):
    return pagetitle in self.driver.title
  
  #打开页面,并校验页面链接是否加载正确
  #以单下划线_开头的方法,在使用import *时,该方法不会被导入,保证该方法为类私有的。
  def _open(self, url, pagetitle):
    #使用get打开访问链接地址
    self.driver.get(url)
    self.driver.maximize_window()
    #使用assert进行校验,打开的窗口title是否与配置的title1致。调用on_page()方法
    assert self.on_page(pagetitle), u"打开开页面失败 %s"%url
  
  #定义open方法,调用_open()进行打开链接
  def open(self):
    self._open(self.base_url, self.pagetitle)
  
  #重写元素定位方法
  def find_element(self,*loc):
#    return self.driver.find_element(*loc)
    try:
      #确保元素是可见的。
      #注意:以下入参为元组的元素,需要加*。Python存在这种特性,就是将入参放在元组里。
#      WebDriverWait(self.driver,10).until(lambda driver: driver.find_element(*loc).is_displayed())
      #注意:以下入参本身是元组,不需要加*
      WebDriverWait(self.driver,10).until(EC.visibility_of_element_located(loc))
      return self.driver.find_element(*loc)
    except:
      print u"%s 页面中未能找到 %s 元素"%(self, loc)
  
  #重写switch_frame方法
  def switch_frame(self, loc):
    return self.driver.switch_to_frame(loc)
  
  #定义script方法,用于执行js脚本,范围执行结果
  def script(self, src):
    self.driver.execute_script(src)
  
  #重写定义send_keys方法
  def send_keys(self, loc, vaule, clear_first=True, click_first=True):
    try:
      loc = getattr(self,"_%s"% loc) #getattr相当于实现self.loc
      if click_first:
        self.find_element(*loc).click()
      if clear_first:
        self.find_element(*loc).clear()
        self.find_element(*loc).send_keys(vaule)
    except AttributeError:
      print u"%s 页面中未能找到 %s 元素"%(self, loc)

2. Define the basic operation method of login page.

All page element positioning is defined in this layer, UI1 once there is a change, only need to modify this layer of page object attributes.

Named test_8_3_2_LoginPage. py


# coding=utf-8
'''
Created on 2016-8-13
@author: Jennifer
Project: Basic operation methods of the page: such as open , input_username , input_password , click_submit
'''
from selenium.webdriver.common.by import By
from test_8_3_2_BasePage import BasePage

# Inheritance BasePage Class 
class LoginPage(BasePage):
  # Locator, which locates the element object through the element attribute 
  username_loc =(By.NAME,'email')
  password_loc =(By.NAME,'password')
  submit_loc =(By.ID,'dologin')
  span_loc =(By.CSS_SELECTOR,"div.error-tt>p")
  dynpw_loc =(By.ID,"lbDynPw")
  userid_loc =(By.ID,"spnUid")
  
  # Operation 
  # Override by inheritance ( Overriding ) Method: If the subclass and the parent have the same method name, the subclass's own method is preferred. 
  # Open Web Page 
  def open(self):
  # Call page In _open Open a connection 
    self._open(self.base_url, self.pagetitle)
  
  # Enter the user name: call send_keys Object, enter a user name 
  def input_username(self, username):
#    self.find_element(*self.username_loc).clear()
    self.find_element(*self.username_loc).send_keys(username)
  
  # Enter password: call send_keys Object, enter the password 
  def input_password(self, password):
#    self.find_element(*self.password_loc).clear()
    self.find_element(*self.password_loc).send_keys(password)
    
  # Click Login: Call send_keys Object, click Login 
  def click_submit(self):
    self.find_element(*self.submit_loc).click()
  
  # Unreasonable username or password is Tip Box content display 
  def show_span(self):
    return self.find_element(*self.span_loc).text
  
  # Switch login mode to dynamic password login ( IE Valid under) 
  def swich_DynPw(self):
    self.find_element(*self.dynpw_loc).click()
  
  # Users on the Logon Success page ID Find 
  def show_userid(self):
    return self.find_element(*self.userid_loc).text

3. Writing test cases using the unittest framework


# coding=utf-8
'''
Created on 2016-8-13
@author: Jennifer
Project: Use unittest Framework to write test cases. 
'''
import unittest 
from test_8_3_2_LoginPage import LoginPage
from selenium import webdriver

class Caselogin126mail(unittest.TestCase):
  """
      Login 126 Mailbox case
  """
  def setUp(self):
    self.driver = webdriver.Firefox()
    self.driver.implicitly_wait(30)
    self.url ="http://mail.126.com"
    self.username ="XXX"
    self.password ="XXX"
  
  # Use case executor 
  def test_login_mail(self):
    # Declaration LoginPage Class object 
    login_page = LoginPage(self.driver, self.url, u" NetEase ")
    # Invoke the Open Page component 
    login_page.open()
    # Switch to the login box Frame
    login_page.switch_frame('x-URS-iframe')
    # Invoke the user name input component 
    login_page.input_username(self.username)  
    # Invoke the password input component 
    login_page.input_password(self.password)    
    # Call the Login button component 
    login_page.click_submit()

  def tearDown(self):
    self.driver.quit()
    
if __name__ == "__main__":
  unittest.main()

Conclusion:

The advantage of this layering is that different layers care about different problems. Page object layer only cares about element positioning, and test cases only care about test data.


Related articles: