页面对象

这章是对 页面对象设计模型的特别指导。一个页面对象代表了你要测试的用户接口交互的区域。

使用页面对象模型的好处: 可以写出能在多个测试案例里复用的代码 减少重复代码 * 如果用户接口更改,只需要在一个地方做相应修改即可

测试案例

下面这个测试案例测试了在python.org网页上搜索一个单词并确认有相应的搜索结果:

import unittest
from selenium import webdriver
import page

class PythonOrgSearch(unittest.TestCase):
    """一个简单展示页面对象如何工作的类"""

    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.get("http://www.python.org")

    def test_search_in_python_org(self):
        """
        测试 python.org网站的搜索功能。搜索一个单词“pycon”然后验证某些结果会展示出来。
        注意这个测试不会在搜索结果页里寻找任何细节文本,它只会验证结果为非空
        """

        #载入主页面,这个例子里是 Python.org的首页
        main_page = page.MainPage(self.driver)
        #检查页面的标题是否包含"python"单词
        assert main_page.is_title_matches(), "python.org title doesn't match."
        #将搜索框的文本设置为"pycon"
        main_page.search_text_element = "pycon"
        main_page.click_go_button()
        search_results_page = page.SearchResultsPage(self.driver)
        #验证结果页非空
            assert search_results_page.is_results_found(), "No results found."

    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main()

页面对象类

页面对象模型旨在给每一个Web页面创造一个对象。运用这个技术我们可以在测试代码和技术实现之间创建一个分离层,page.py会是这样的:

from element import BasePageElement
from locators import MainPageLocators

class SearchTextElement(BasePageElement):
    """这个类从指定的定位器里获取到搜索文本"""

    #已经输入搜索字符串的搜索框的定位器
    locator = 'q'


class BasePage(object):
    """初始化所有页面都会调用的基本页类"""

    def __init__(self, driver):
        self.driver = driver


class MainPage(BasePage):
    """主页操作方法放这里"""

    #定义一个变量存放检索文本
    search_text_element = SearchTextElement()

    def is_title_matches(self):
        """验证硬编码字符"python"出现在页面标题里"""
        return "Python" in self.driver.title

    def click_go_button(self):
        """触发搜索功能"""
        element = self.driver.find_element(*MainPageLocators.GO_BUTTON)
        element.click()


class SearchResultsPage(BasePage):
    """搜索结果页操作方法放这里"""

    def is_results_found(self):
        # 或许应该在具体的页面元素里搜索文本,不过目前为止这样运行没什么问题
        return "No results found." not in self.driver.page_source

页面元素

element.py类大致是这样的:

from selenium.webdriver.support.ui import WebDriverWait


class BasePageElement(object):
    """初始化每个页面对象类的基本页类"""

    def __set__(self, obj, value):
        """用给定的值设置文本"""
        driver = obj.driver
        WebDriverWait(driver, 100).until(
            lambda driver: driver.find_element_by_name(self.locator))
        driver.find_element_by_name(self.locator).send_keys(value)

    def __get__(self, obj, owner):
        """从具体的对象里获取文本"""
        driver = obj.driver
        WebDriverWait(driver, 100).until(
            lambda driver: driver.find_element_by_name(self.locator))
        element = driver.find_element_by_name(self.locator)
        return element.get_attribute("value")

定位器

One of the practices is to separate the locator strings from the place where they are being used.在这个例子里,同页面的定位器是同一个类

The locators.py will look like this:

from selenium.webdriver.common.by import By

class MainPageLocators(object):
    """一个主页面定位器类,所有的页面定位器应该来自这里"""
    GO_BUTTON = (By.ID, 'submit')

class SearchResultsPageLocators(object):
    """一个搜索结果定位器类,所有搜索结果定位器应该来自这里"""
    pass