7. 页面对象开发的第二种实现方式:页面实例化

页面对象开发的第二种实现方式:页面实例化

代码架构解析

1. Page基类设计

class Page:
    url = None
    locators = {}
    browser = CHROME

    def __init__(self, page=None):
        if page:
            self.driver = page.driver
        else:
            self.driver = self.browser().start_chrome_browser

    def __getattr__(self, loc):
        if loc not in self.locators.keys():
            raise Exception
        by, val = self.locators[loc]
        return self.driver.find_element(by, val)
  • 核心机制

    • url:存储页面地址的类属性
    • locators:元素定位器字典(需被子类覆盖)
    • __init__:支持通过已有页面实例共享driver
    • __getattr__:动态属性访问实现元素定位
  • 设计亮点

    • 浏览器实例共享(实现页面跳转)
    • 动态元素定位(通过属性名映射locators字典)
    • 统一驱动管理(自动初始化或复用已有驱动)

2. CommonLoginPage登录模块

class CommonLoginPage(Page):
    url = 'http://secure.smartbearsoftware.com/...'
    locators = {
        'username': ('id','ctl00_MainContent_username'),
        'password': ('id', 'ctl00_MainContent_password'),
        'loginBtn': ('id', 'ctl00_MainContent_login_button')
    }

    def get(self):
        self.driver.get(self.url)

    def login(self, username='Tester', password='test'):
        self.username.send_keys(username)
        self.password.send_keys(password)
        self.loginBtn.click()
  • 功能实现
    • 继承Page基类的基础能力
    • 定义具体页面的元素定位器
    • 封装页面访问和登录操作
    • 通过属性访问元素(如self.username)

3. MainPage主界面模块

class MainPage(CommonLoginPage):
    CommonLoginPage.locators.update({
        'clickOrder': ('xpath', '//*[@id="ctl00_menu"]/li[3]/a'),
        'orderInput': ('id', 'ctl00_MainContent_fmwOrder_txtName'),
        'clickProcess': ('id', 'ctl00_MainContent_fmwOrder_InsertButton'),
        'bug_label': ('id',"ctl00_MainContent_fmwOrder_RequiredFieldValidator3"),
        'order_label': ('xpath','//*[@id="aspnetForm"]//td[1]/h1')
    })

    def search_bug(self, order_input='Tom'):
        self.clickOrder.click()
        self.orderInput.send_keys(order_input)
        self.clickProcess.click()
  • 扩展机制
    • 通过locators.update合并父类定位器
    • 添加订单操作相关元素定位
    • 实现缺陷搜索业务流程
    • 继承复用登录功能

4. TestMain测试执行类

class TestMain:
    def test_login(self):
        page = MainPage()
        page.get()
        page.login()
        assert page.order_label.text == 'Web Orders'
        page.driver.quit()

    def test_search(self):
        page = MainPage()
        page.get()
        page.login()
        page.search_bug()
        sleep(4)
        assert page.bug_label.text == "Field 'Street' cannot be empty."
        page.driver.quit()
  • 测试流程
    • 创建页面实例时自动初始化浏览器
    • 链式调用页面操作方法
    • 通过页面属性访问元素进行断言
    • 显式管理浏览器生命周期

类关系图示

«Abstract»
Page
+ url
+ locators
+ browser
+__init__()
+__getattr__()
CommonLoginPage
+get()
+login()
MainPage
+search_bug()
TestMain
+test_login()
+test_search()

关键设计模式

1. 动态属性映射

访问page.username
属性存在?
返回属性值
触发__getattr__
检查locators字典
执行元素定位
返回WebElement

2. 页面实例传递

# 页面跳转示例
class OrderPage(Page):
    def goto_detail(self):
        return OrderDetailPage(page=self)  # 传递当前页面实例

3. 定位器继承机制

class BasePage(Page):
    locators = {'header': ('css', '.header')}

class HomePage(BasePage):
    locators = BasePage.locators.copy()
    locators.update({'banner': ('id', 'banner')})

理解上面代码,代码继续优化

1. 定位器管理改进

# 使用类属性合并代替直接修改
class MainPage(CommonLoginPage):
    locators = {**CommonLoginPage.locators, **{
        'clickOrder': ('xpath', '...'),
        'orderInput': ('id', '...')
    }}

2. 异常处理优化

def __getattr__(self, loc):
    if loc not in self.locators:
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{loc}'")
    by, val = self.locators[loc]
    try:
        return self.driver.find_element(by, val)
    except NoSuchElementException:
        raise ElementNotFoundError(loc)

3. 等待机制集成

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def __getattr__(self, loc):
    # ...
    return WebDriverWait(self.driver, 10).until(
        EC.presence_of_element_located((by, val))

方案优缺点对比

优势

  • 减少样板代码:通过动态属性访问简化元素定位
  • 增强可维护性:集中管理元素定位器
  • 支持页面跳转:通过实例共享实现跨页面操作

注意事项

  • 定位器字典需要严格维护
  • 动态属性可能掩盖拼写错误
  • 需要合理设计继承体系

全套代码

"""
Python :3.13.3
Selenium: 4.31.0
"""

from chap.ob import *



class Page:

    url = None
    locators = {}
    browser = CHROME

    def __init__(self, page=None):
        if page:
            self.driver = page.driver
        else:
            self.driver = self.browser().start_chrome_browser

    def __getattr__(self, loc):
        if loc not in self.locators.keys():
            raise Exception

        by, val = self.locators[loc]

        return self.driver.find_element(by, val)


class CommonLoginPage(Page):
    url = 'http://secure.smartbearsoftware.com/samples/testcomplete12/WebOrders/Login.aspx'
    locators = {
        'username':('id','ctl00_MainContent_username'),
        'password': ('id', 'ctl00_MainContent_password'),
        'loginBtn':('id', 'ctl00_MainContent_login_button')
    }

    def get(self):
        """
        打开首页地址
        :return:
        """
        self.driver.get(self.url)

    def login(self, username: str = 'Tester', password: str = 'test'):
        self.username.send_keys(username)
        self.password.send_keys(password)
        self.loginBtn.click()


class MainPage(CommonLoginPage):
    CommonLoginPage.locators.update({
        'clickOrder': ('xpath', '//*[@id="ctl00_menu"]/li[3]/a'),
        'orderInput': ('id', 'ctl00_MainContent_fmwOrder_txtName'),
        'clickProcess': ('id', 'ctl00_MainContent_fmwOrder_InsertButton'),
        'bug_label': ('id',"ctl00_MainContent_fmwOrder_RequiredFieldValidator3"),
        'order_label': ('xpath','//*[@id="aspnetForm"]//td[1]/h1')

    })

    def search_bug(self, order_input: str = 'Tom'):
        self.clickOrder.click()
        self.orderInput.send_keys(order_input)
        self.clickProcess.click()



class TestMain:
    """
    测试登录和检索bug功能
    """
    def test_login(self):
        page = MainPage()
        page.get()
        page.login()
        assert page.order_label.text == 'Web Orders'
        print('test_login is passed')
        page.driver.quit()


    def test_search(self):
        page = MainPage()
        page.get()
        page.login()
        page.search_bug()
        from time import sleep
        sleep(4)
        assert page.bug_label.text == "Field 'Street' cannot be empty."
        print('test_search is passed')
        page.driver.quit()


obj = TestMain()
obj.test_login()
obj.test_search()

“好的页面对象设计应该像使用真实网页一样自然”


「小贴士」:点击头像→【关注】按钮,获取更多软件测试的晋升认知不迷路! 🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值