Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

Python加Selenium自动化测试知乎网站(三)页面对象模式



页面对象(Page Object)模式是做自动化测试中用到的一种模式理念,本质上是为了复用代码和提高可维护性。页面对象是将独立页面封装为一个或多个类,将页面上的元素做统一管理,将页面涉及到的业务点做封装以便不同脚本调用。在测试脚本中也能体现出业务点,而不是千篇一律的点击输入。

比如注册页面,封装出注册的业务方法,并将相应的元素都整合在一起,如果页面发生变化,只需要更新这一个文件即可,而不用到处搜索替换。

但是有些业务是跨页面的,比如保险行业里的填写保单,是分为多个步骤分别对应不同的页面提交数据,相应的页面对象可以组合为一个工作业务流。在调用不同的方法时,也能看得出当前的业务流程。

介绍了页面对象,我们来优化上一篇文章中的代码,将元素和通用方法抽取出来。

上一篇的例子中会涉及到全局的导航栏、话题页面和搜索结果页面,我们分别来定义三个页面对象。

先新建utils/yaml_helper.py, 用来处理yaml文件中管理的元素定义:

import yaml

class YamlHelper:

    @classmethod
    def load_yaml(cls, file_path):
        with open(file_path, "r") as f:
            data = yaml.load(f, Loader=yaml.SafeLoader)
        return data

然后创建一个package比如叫pages,pages里创建一个文件夹叫elements用来管理页面元素。

定义一个基础类在page_base.py中:

from utils.yaml_helper import YamlHelper
from selenium.webdriver.common.by import By
import os


class PageBase:

    def __init__(self, driver):
        self._driver = driver
        self._elements = None
        element_file = os.path.join(os.getcwd(), "pages", "elements", "%s.yaml" % self.__module__.split(".")[-1])
        if os.path.exists(element_file):
            self._elements = YamlHelper.load_yaml(element_file)

    def element(self, name, *args):
        by, value = self.format_locator(self._elements[name], *args)
        return self._driver.find_element(by, value)

    def elements(self, name, *args):
        by, value = self.format_locator(self._elements[name], *args)
        return self._driver.find_elements(by, value)

    def format_locator(self, locator, *args):
        web_locators = list(By.__dict__.items())
        by = value = None
        for k, v in web_locators:
            if v == locator["by"]:
                by = v
                value = locator["value"].format(*args)
                break
        return by, value

在pages/elements中新建3个yaml文件,填入元素定义:

## topic_page.yaml
focus_topic_button:
  by: xpath
  value: "//div[@class='TopicActions TopicMetaCard-actions']/button[contains(@class, 'TopicActions-followButton')]"

topics:
  by: xpath
  value: "//div[@class='List-item TopicFeedItem']/div[@class='ContentItem ArticleItem']"

login_username_field:
  by: xpath
  value: "//div[@class='SignContainer-content']//input[@name='username']"

popup_close_button:
  by: xpath
  value: "//button[@class='Button Modal-closeButton Button--plain']"
## header.yaml
search_field:
  by: xpath
  value: "//div[contains(@class, 'AppHeader-SearchBar')]//input"

search_button:
  by: xpath
  value: "//div[contains(@class, 'AppHeader-SearchBar')]//button[contains(@class, 'SearchBar-searchButton')]"
## search_result_page.yaml
search_result_title_by_index:
  by: xpath
  value: "//div[@id='SearchMain']//div[@class='Card SearchResult-Card'][{0}]//h2[@class='ContentItem-title']/a"

新建3个对应的页面对象文件:

## topic_page.py

from pages.page_base import PageBase


class TopicPage(PageBase):

    def count_topics_displayed(self):
        return self.elements("topics")

    def focus_topic_without_login(self):
        self.element("focus_topic_button").click()
        self.element("login_username_field").send_keys("123456")
        self.element("popup_close_button").click()
## header.py

from pages.page_base import PageBase


class Header(PageBase):

    def search(self, keyword):
        self.element("search_field").send_keys(keyword)
        self.element("search_button").click()
## search_result_page.py

from pages.page_base import PageBase


class SearchResultPage(PageBase):

    def retrieve_first_result_title(self):
        return self.element("search_result_title_by_index", 1).text

于是在第二篇文章中的脚本就可以优化为如下内容:

## 这是加上了页面对象的样例,但仍然有缺点,会继续在后续文章中优化
## 操作步骤仍然是话题页关注并搜索关键词检查内容
from selenium import webdriver
from pages.topic_page import TopicPage
from pages.header import Header
from pages.search_result_page import SearchResultPage

topic_url = "https://www.zhihu.com/topic/19552832/hot"
keyword = "用Django做一个简单的记账网站"

driver = webdriver.Chrome()
driver.get(topic_url)
topic_page = TopicPage(driver)
header = Header(driver)
search_result_page = SearchResultPage(driver)

topics_amount = topic_page.count_topics_displayed()
assert len(topics_amount) >= 5
topic_page.focus_topic_without_login()
header.search(keyword)
first_search_result_title = search_result_page.retrieve_first_result_title()
assert first_search_result_title == keyword
driver.close()
driver.quit()

现在的样例是不是看起来清爽了一些?变得简单了一点,元素能统一管理和维护,提高了脚本可读性,有封装复用可以看到页面跳转。但仍然有缺点,它并不稳定。接下来会描写等待机制以提高稳定性。

下一篇:  Python中的protected和private
上一篇:  Python加Selenium自动化测试知乎网站(二)操作页面元素

共有0条评论

添加评论

暂无评论