什么是PO模式

  • PO模式是page object model的缩写,是一种设计模式

  • 把待测页面当成一个页面对象,一般包含了元素对象的定位和元素操作方法,将页面对象和真实的网站页面一 一映射起来

  • Page Object模式主要是将每个页面设计为一个类class,这个类包含页面中需要测试的元素(按钮、输入框、URL、标题等)和实际操作方法,这样在写测试用例时可以通过调用页面类的方法和属性来获取页面元素和操作元素,这样优点是避免当页面元素的ID或位置改变时需要更改测试用例代码的情况。当页面元组定位发生改变时只要通过更改页面类的属性即可。

    • 一个类中,一个方法调用另一个方法时,需要加self.被调用的方法(self.入参)
    • 在Python方法中入参是元组时,需要加*,因为Python存在这种特性,将入参放进元组里,入参是元组的元素需要加*。
    • 把操作方法封装为函数时,return返回就是这个操作方法的具体操作,return返回值可以给其他函数直接使用的。

PO模式有哪些特征?

  • 页面封装成Page 类,页面元素为Page 类的成员元素,页面功能的实现放在Page 类的方法里。
  • 将一个待测页面(或者待测试对象)封装成一个类(Class),例如:登录页面,把它称作Page 类。Page 类里包括了这个页面(或者待测试对象)上的所有的元素(登录页面的url链接,username/password文本框的输入,图片验证码的获取,登录/注册button的点击),以及针对页面元素的操作方法(单步操作或者多步操作,一般会定义类方法)。类属性一般放页面的特征元素:如url,标题等
  • 注意:这个登录页面的Page 类里仅仅包括登录页面,一般不包括针对(除去登录页面)其他页面的操作。
  • 针对这个Page 类定义一个测试类,在测试类调用Page 类的各个类方法完成自动化测试。也就是测试代码和被测试页面的页面代码解耦,当页面本身发生变化,例如:元素定位发生改变、页面布局改变后,仅需要更改相对应的Page 类的代码,而无须更改测试类的代码。PageObject 模式减少了代码冗余,可以使业务流程变得清晰易读,降低了测试代码维护成本。

PO模式的分层

PO模式可以把一个页面一般分为三层

对象库层:二次封装Selenium的方法

页面操作层(也称:逻辑层):封装页面的元素对象和元素操作

测试层(也称:业务层):多个页面操作完成一个业务测试,一般结合单元测试框架(unittest、pytest)来测试

当然也有分四层的

对象库层:二次封装Selenium的方法

页面层:封装页面的元素对象

操作层:封装页面的元素操作

测试层:多个页面操作完成一个业务测试

PO模式给代码带来的优势

  • 代码冗余明显降低:二次封装Selenium方法和提取公共方法,提高代码复用性
  • 代码的阅读性明显提升:因为三层分级,将不同内容进行不同的封装,整体代码阅读性提升
  • 代码维护性明显提升:UI测试中,页面若经常变动,代码的维护量随之增多;因为三层分级,我们只需要修改页面对象的代码,如元素对象或者操作对象的方法,不用修改测试用例的代码,也不影响测试用例的正常执行
  • 降低代码耦合性

po模式.png

代码示例

1. 封装“课堂派”的登录页page

# 目录结构
├─pages
│      login_page.py
│      __init__.py

login_page.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/20 20:34
# @Author  : shisuiyi
# @File    : login_page.py
# @Software: win10 Tensorflow1.13.1 python3.9
from selenium.webdriver.common.by import By

import setting


class LoginPage:
    """登录页面"""
    url = setting.host + '/User/login.html'

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

    def reload(self):
        """刷新"""
        self.driver.refresh()

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

    def login(self, username, password):
        """登录"""
        # 输入用户户名
        self.driver.find_element(By.XPATH, '//input[@name="account"]').send_keys(username)

        # 输入密码
        self.driver.find_element(By.XPATH, '//input[@name="pass"]').send_keys(password)

        # 点击登录
        self.driver.find_element(By.XPATH, '//a[@class="btn-btn" and text()="登录"]').click()

    def logout(self):
        """退出登录"""
        pass

    def forget_password(self):
        """忘记密码"""
        pass

    def get_error_msg(self):
        """获取登录失败的错误信息"""
        return self.driver.find_element(By.XPATH, '//p[@class="error-tips"]').text

2. 前置fixture

conftest.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/15 14:14
# @Author  : shisuiyi
# @File    : conftest.py.py
# @Software: win10 Tensorflow1.13.1 python3.9
import pytest
from selenium import webdriver


@pytest.fixture(scope='session')
def driver():
    # 养成设置隐性
    driver = webdriver.Chrome()
    driver.implicitly_wait(8)
    driver.maximize_window()
    yield driver
    driver.quit()

3. 配置文件

setting.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/20 20:31
# @Author  : shisuiyi
# @File    : setting.py
# @Software: win10 Tensorflow1.13.1 python3.9、


# host
host = 'https://v4.ketangpai.com'

4. 定义一个登录测试类

test_login.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/13 19:46
# @Author  : shisuiyi
# @File    : test_login.py
# @Software: win10 Tensorflow1.13.1 python3.9
import pytest

from pages.login_page import LoginPage

ids = ['case1', 'case2', 'case3']


@pytest.mark.parametrize('username,password,expected',
                         [['123', 'abc', '密码有效长度是6到30个字符'],
                          ['123', '12345678', '用户名或密码无效'],
                          ['1884****463', '1234568', '密码错误']], ids=ids)
class TestLogin(object):
    """测试登录功能"""

    def test_login(self, username, password, expected, driver):
        login_page = LoginPage(driver)
        login_page.load()
        login_page.login(username, password)
        actual = login_page.get_error_msg()
        assert expected in actual

5. 运行测试用例

run.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/12 20:41
# @Author  : shisuiyi
# @File    : run.py
# @Software: win10 Tensorflow1.13.1 python3.9
import pytest

pytest.main(['--alluredir=report'])

6. 测试报告

测试报告.png