使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 21--PO(POM) 设计模式代码实现和全局浏览器驱动设置

测试学习记录,仅供参考!

一、 PO(POM)设计模式代码实现

管理页面类和页面元素

1、在项目根目录下新建名称为 pageObject(用来管理页面类) 的软件包,在 pageObject 软件包下新建名称为 login_page(登录页面) 的目录文件,在 login_page 目录下新建名称为 login_page.py 的 Python 文件;

定义一个测试类,因为 class LoginPage 是一个页面类,所以不需要以 test 开头;这个页面类里面去维护一些元素定位和操作,首先得先导入定位元素 By 类 ,这里写一个类变量,使用类属性,不需要 __init__ 初始化为实例属性,一般正常写法是“self.driver.find_element(By.NAME, 'username')”,但是每一个都这么写的话很不方便,此时需要引入前面介绍过的二次封装(找到元素方法)方法,导入 BasePage 类,然后使用页面类去继承 BasePage 类(这样页面类就可以用到已经封装好的公共方法了)

把被测试页面的 url 地址、用户名、密码、登录按钮等都放到页面类里面管理,先完成“元素”,再继续“操作”,例如:第一个 登录操作,定义一个 def login(): 函数,登录里面带两个参数(self, user_name, pass_word):就是要输入的操作,输入的用户名(user_name)和密码(pass_word),使用 self.open_url()(因为页面类已经继承了公共的webdriver 二次封装方法,所以可以使用 BasePage 类里面的任何属性和方法) 打开页面,里面把 url 传过来,还是通过 self.url 调用这个 LoginPage 类 url 变量(self.url 中的 self 是表示类本身的意思),下一步跟着输入操作(输入操作亦已经封装了,同样的通过 self. 点调用,注意二次封装方法的参数若有默认值的可以不填)

为什么要对 webdriver 里面的方法进行二次封装,为了页面类使用的时候更方便,不用每一个都去写一个“去查找、去定位 driver.find_element(By.NAME, 'username')”,简化了代码量,这就是二次封装的一个好处;

from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage

class LoginPage(BasePage):
    url = 'http://localhost:8088/ecshop/user.php'
    # 用户名
    username = (By.NAME, 'username')
    # 密码
    password = (By.NAME, 'password')
    # 登录按钮
    submit = (By.NAME, 'submit')

    # 登录操作
    def login(self, user_name, pass_word):
        # 打开网址
        self.open_url(self.url)
        # 输入用户名
        self.send_keys(self.username, user_name)
        # 输入密码
        self.send_keys(self.password, pass_word)
        # 点击登录按钮
        self.click(self.submit)

至此,登录页面的第一个操作“登录操作”就已经封装完成了;

管理测试类

2、改造 testcase 软件包 login 目录下 test_login.py 文件;

先导包引入 LoginPage 类,登录成功里面的‘打开浏览器’、‘定位信息’、已经在上一步的页面类中维护了(元素),所以不需要了;一些输入的操作也是在页面类(操作)中维护,亦不需要了;这时候需要直接用到页面类,把导进来的 LoginPage 类直接用就行了;

from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage

class TestLogin:

    # 前置操作
    def setup(self):
        # 前面加上 self. 把它变成全局的,就可以在其他测试用例中调用这个对象了
        self.driver = webdriver.Edge()

    # 登录成功
    def test_login_success(self):
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(self.driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(后续参数化)
        login_page.login('admin123', '123456')

        # 断言结果
        success_ele = self.driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')
        assert success_ele != ''
        sleep(2)

    # 登录失败
    def test_login_failed(self):
        login_page = LoginPage(self.driver)
        login_page.login('admin123', '123456789')

        # 断言结果
        assert '系统提示' in self.driver.title
        sleep(2)

    # 后置操作
    def teardown(self):
        # 关闭浏览器
        self.driver.quit()

现在,测试类里面的一些操作就不用关心 ‘元素定位信息和操作了’,只需要去调用页面类里面的操作就行了;

写这个的好处就是 后续某个页面发生变更时,只需维护某个页面相关的定位即可;这就是 POM 的设计模式,简单理解就是“页面、对象、分离”;

3、后面使用参数化通过一个测试用例实现多种场景;

4、这是第一个页面‘登录页面(login_page)’,后续其他页面可以继续编写,例如第二个页面‘注册页面(register_page)’等等;

其他

在 pageObject 软件包下新建名称为 register_page(注册 页面) 的目录文件,在 register_page 目录下新建名称为 register_page.py 的 Python 文件;

与之相对应的,在 testcase 软件包下新建 register 目录文件,在 register 目录下 test_register.py 文件,用于编写注册页面的测试脚本;达到‘页面元素定位’、‘操作’和‘测试脚本’分离,这是一个稍微双耦合的效果;

二、使用Pytest设置全局的浏览器驱动对象

class TestCase:

    # 前置操作
    def setup(self):
        self.driver = webdriver.Edge()
        
    # 后置操作
    def teardown(self):
        self.driver.quit()

1、若在每一个测试用例里面均写上前后置操作,如果测试用例有很多个,几百上千个等等,要是每一个都写上前后置操作就会比较麻烦,所以对前后置部分做一个改造;优化前后置操作,使用 conftest.py 和 fixture 装饰器相结合实现全局的前后置应用;

2、找到 testcase 软件包下的 conftest.py 文件(若无此文件则新建一个),开始优化;

写一个前置操作 def get_driver(): 获取浏览器对象

import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

@pytest.fixture()
def get_driver():
    #  初始化浏览器对象
    driver = webdriver.Chrome()
    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(10)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    # 后置操作--关闭浏览器
    driver.quit()

3、在 test_login.py 调用前后置 def get_driver(): 操作;进一步优化;

from selenium.webdriver.common.by import By
from time import sleep
from pageObject.login_page.login_page import LoginPage

class TestLogin:

    # 登录成功
    def test_login_success(self, get_driver):
        # 传浏览器对象--再把结果返回
        login_page = LoginPage(get_driver)
        # 直接调用页面类中的 login 操作--里面需要输入两个参数(后续参数化)
        login_page.login('admin123', '123456')
        # 断言结果
        success_ele = get_driver.find_element(By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')
        assert success_ele != ''
        sleep(2)

    # 登录失败
    def test_login_failed(self, get_driver):
        login_page = LoginPage(get_driver)
        login_page.login('admin123', '123456789')
        # 断言结果
        assert '系统提示' in get_driver.title
        sleep(2)

优化之后,测试类中就比较简洁了,就写了两个测试用例;把其他操作都放到 conftest.py 文件中的前后置操作中;

4、运行主函数 run.py 文件可以查看到刚刚添加的浏览器最大化;

5、把初始化浏览器做到配置文件中;

6、更改项目根目录 config 软件包下 setting.py 文件;

# 导包
import os
import sys

DIR_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(DIR_PATH)

# 显示等待时间设置,默认为10秒
WAIT_TIME = 10

# 设置浏览器
browser_type = 'chrome'

# 文件路径
FILE_PATH = {
    'log': os.path.join(DIR_PATH, 'log'),
    'screenshot': os.path.join(DIR_PATH, 'screenshot')
}

7、再优化一下 conftest.py 文件;

导入刚刚定义的 browser_type 变量,在初始化浏览器对象时做个 if 判断 browser_type 这个变量 .点 调用 capitalize() 函数(首字母转成大写) == 等于 'Chrome'、'Edge'、'Firefox' 成立的话,就初始化相对应的 'Chrome'、'Edge'、'Firefox' 浏览器对象;后续需要用到哪个浏览器做测试,就更改 setting.py 文件中的 browser_type 变量即可;

import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

@pytest.fixture()
def get_driver():
    #  初始化浏览器对象
    if browser_type.capitalize() == 'Chrome':
        driver = webdriver.Chrome()
    elif browser_type.capitalize() == 'Edge':
        driver = webdriver.Edge()
    elif browser_type.capitalize() == 'Firefox':
        driver = webdriver.Firefox()

    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(10)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()

8、还可以继续优化 conftest.py 文件;把代码写的稍微好看一点,不需要很多 if else elif 的;使用字典的方式去替代 if else 嵌套;

import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

@pytest.fixture()
def get_driver():
    #  初始化浏览器对象--字典
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }

    # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面
    if browser_type.capitalize() in browser_mapping:
        driver = browser_mapping.get(browser_type.capitalize())()

    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(10)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()

判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面,就可以使用 browser_mapping 字典去调用 .get() 方法,把 browser_type.capitalize() 放到 .get(browser_type.capitalize()) 方法里面;注意后面再带上一个 () 小括号,因为定义字典时没写括号,所以这里带括号并且引用它,把结果返回出去;

@pytest.fixture()
def get_driver():
    #  初始化浏览器对象--if判断
    if browser_type.capitalize() == 'Chrome':
        driver = webdriver.Chrome()
    elif browser_type.capitalize() == 'Edge':
        driver = webdriver.Edge()
    elif browser_type.capitalize() == 'Firefox':
        driver = webdriver.Firefox()

    # 字典
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }

    # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面
    if browser_type.capitalize() in browser_mapping:
        driver = browser_mapping.get(browser_type.capitalize())()

    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(10)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()

9、通过配置文件读取等待时间,不写固定,导入引用;

import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type, WAIT_TIME

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

@pytest.fixture()
def get_driver():
    #  初始化浏览器对象--字典
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }

    # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面
    if browser_type.capitalize() in browser_mapping:
        driver = browser_mapping.get(browser_type.capitalize())()

    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(WAIT_TIME)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()

10、黄色警告,作用域问题

11、设置全局变量;

import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type, WAIT_TIME

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

@pytest.fixture()
def get_driver():

    # 将driver设置为全局变量
    global driver

    #  初始化浏览器对象--字典
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }

    # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面
    if browser_type.capitalize() in browser_mapping:
        driver = browser_mapping.get(browser_type.capitalize())()

    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(WAIT_TIME)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值