Skip to content

PageObject 设计模式


简介

马丁福勒个人博客

Alt text

selenium 官网


传统 UI 自动化的问题

  • 无法适应 UI 频繁变化
  • 无法清晰表达业务用例场景
  • 大量的样板代码 driver/find/click

PageObject 模式的优势

  • 降低 UI 变化导致的测试用例脆弱性问题
  • 让用例清晰明朗,与具体实现无关

PageObject 建模原则


PO 设计模式 6 大原则

  • 字段意义
    • 不要暴露页面内部的元素给外部
    • 不需要建模 UI 内的所有元素
  • 方法意义
    • 用公共方法代表 UI 所提供的功能
    • 方法应该返回其他的 PageObject 或者返回用于断言的数据
    • 同样的行为不同的结果可以建模为不同的方法
    • 不要在方法内加断言

1.不暴露页面内部元素给外部

不要暴露很多细节,我们的这个方法内是可以有细节的,但是方法它一定是一个接口的形式。这些网页上的元素,抽象成 PageObject 的属性,在 python 中,我们可以设置成内部属性,通常属性名称前,用一个或者两个下划线开头,表示这是非公开的属性。


2.不要建模 UI 内的所有元素

我们不要把页面中的每个元素进行建模,如果说这个页面中有非常多的功能,同样可能有几千上万个页面元素。如果为整个页面都去建一个模,这样会非常的繁琐复杂,是很不现实的。我们要学会挑重点,只需要为重要的元素进行建模就好了。


3.用公共方法代表 UI 所提供的功能

我们公共方法应该去代替一个页面的服务,这个怎么理解呢?我们以登录场景为例,在登陆页面通常需要填写用户名,密码,然后点击登录,甚至有的还需要填写验证码。那么我们只需要让登录页面,提供一个登录方法,例如叫 login(),然后具体如何去输入用户名、输入密码、点击登录的动作封装在其中,后面我们只需要调用 login()方法就可以实现登录。


4.法应该返回其他的 PageObject,或者返回用于断言的数据

这个是什么意思呢?这个指的是我们页面的公共方法的返回值。在我们的 web 自动化测试过程中,当一个页面进行了一些操作之后,通常会跳到其他的页面,比如登录页面登录成功后,通常会跳到系统的欢迎页,或者首页。这就是一个典型的页面跳转。那么如果说,这个场景已经执行完毕了,我们需要获得一些页面的反馈信息,用来帮助我们判断当前页面是否正确展示了对应的元素信息,那么就可以暴露一个返回元素信息的方法,这就是所讲的,方法可以返回用于断言的数据。


5.同样的行为,不同的结果可以建模为不同的方法

一个页面可能会有相同的动作,还是以登录为例,有时候我们需要测试登录成功,也要测试登录失败,那么就要封装成两个不同的方法,两个方法分别代表登录成功和登录失败,而不是写在一个方法里。一个方法只代表一种特定结果。


6.不要在方法内加断言

这个原则要求我们,将页面功能和测试用例解耦,断言是测试用例该做的时候,一定要和业务逻辑分开。不能写在一起。


POM 使用方法

  • 把元素信息和操作细节封装到 PageObject 类中
  • 根据业务逻辑,在测试用例中链式调用

示例展示


搜索场景:传统线性脚本(Python)

  • 传统测试用例
# test_search.py

from selenium import webdriver
from selenium.webdriver.common.by import By


class TestSearch:

    def test_search(self):

        # 初始化浏览器
        self.driver = webdriver.Chrome()
        self.driver.get("https://xueqiu.com/")
        self.driver.implicitly_wait(3)

        # 输入搜索关键词
        self.driver.find_element(By.NAME, "q").send_keys("阿里巴巴-SW")
        # 点击搜索按钮
        self.driver.find_element(By.CSS_SELECTOR, "i.search").click()
        # 获取搜索结果
        name = self.driver.find_element(By.XPATH, "//table//strong").text
        # 断言
        assert name == "阿里巴巴-SW"

搜索场景:POM 脚本(Python)

  • 股票页面 PageObject
# search_page.py

from selenium import webdriver
from selenium.webdriver.common.by import By


class SearchPage:

    __INPUT_SEARCH = (By.NAME, "q")
    __BUTTON_SEARCH = (By.CSS_SELECTOR, "i.search")
    __SPAN_STOCK = (By.XPATH, "//table//strong")

    def __init__(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)
        self.driver.get("https://xueqiu.com/")

    def search_stock(self, stock_name: str):
        self.driver.find_element(*self.__INPUT_SEARCH).send_keys(stock_name)
        self.driver.find_element(*self.__BUTTON_SEARCH).click()
        name = self.driver.find_element(By.XPATH, "//table//strong").text
        return name

搜索场景:POM 脚本(Java)

  • 股票页面 PageObject
# search_page.py
public class demoTest {
   static WebDriver driver;
    @AfterAll
    static void teardown(){

    }
    @BeforeAll
    static void setup(){
       driver = new ChromeDriver();
    }

    @Test
    void demo(String stock_name ){
      driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
      driver.get("https://xueqiu.com/")
      driver.findElement(By.name("q") ).send_keys(stock_name);

      driver.findElement(By.cssSelector(".search")).click();

      String name = driver.findElement(By.Xxpath("//table//strong")).text
      return name



    }


}

搜索场景:测试用例(Python)

  • PO 模式测试用例
# test_search.py


from onSelenium.fei.page_objects.search_page import SearchPage


class TestSearch:

    def test_search(self):

        text = SearchPage().search_stock("阿里巴巴-SW")

        # 断言
        assert "阿里巴巴-SW" == text

总结

总的来说,使用 Page Object 模式的测试用例具有更高的可读性、可维护性和代码复用性。