UI自动化之混合框架

news2025/1/10 23:35:58

什么是混合框架,混合框架就是将数据驱动与关键字驱动结合在一起,主要用来回归业务主流程,将核心流程串联起来。

上一篇我们写到了关键字驱动框架,关键字驱动框架是针对一个业务场景的单条测试用例的。

我们以163邮箱的登录到创建联系人这个流程为例,来看看混合框架是怎样的。

首先准备一个存放测试用例和数据的excel文件,文件内容如下:

测试用例的sheet页:case

mock表示这条测试用例我们需要用到的框架模型,key表示关键字,data表示数据

step_sheet表示这条用例我们需要用到的关键字驱动的sheet页名称

data_sheet表示这条用例我们需要用到的数据驱动的sheet页名称

login_step页:

add_person_step页:添加联系人的步骤

add_person_data页:添加联系人所需要用到的数据

excel的准备工作就完成了,接下来看代码:

首先是项目目录:只写了简单的几个目录,其他的目录在pageobject三层架构中写过,可以参考,都是一样的。

Setting文件夹的Config.py文件:

# Config.py
import os

Base_Dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 测试数据文件
Test_Data_Path = os.path.join(Base_Dir, 'TestData')

Util文件夹的find_ele.py文件:

# find_ele.py
from selenium.webdriver.support.wait import WebDriverWait


def find_element(driver, location_type, location_express):
    '''查找控件元素'''
    try:
        driver = WebDriverWait(driver, 20).until(lambda driver:driver.find_element(location_type, location_express))
        return driver
    except Exception as e:
        raise e


def find_elements(driver, location_type, location_express):
    '''查找元素组'''
    try:
        driver = WebDriverWait(driver, 20).until(lambda driver:driver.find_elements(location_type, location_express))
        return driver
    except Exception as e:
        raise e

Util文件夹的excel_parse.py文件:读取excel的内容

# excel_parse.py
from Setting.Config import Test_Data_Path
from openpyxl import load_workbook

class ExcelParse:

    def __init__(self):
        self.workbook = None
        # self.sheet = None

    def load_workbook(self, filename):
        '''加载文件'''
        try:
            self.workbook = load_workbook(filename)
        except Exception as e:
            raise e

    def get_sheet(self, sheetname):
        '''获取sheet页'''
        try:
            # self.sheet = self.workbook[sheetname]
            return self.workbook[sheetname]
        except Exception as e:
            raise e

    def get_row_num(self, sheet):
        '''返回行数'''
        # return self.sheet.max_row
        return sheet.max_row

    def get_col_num(self, sheet):
        '''返回列数'''
        # return self.sheet.max_column
        return sheet.max_column

    def get_cell_value(self, sheet, row, col):
        '''返回某一单元格的值'''
        # return self.sheet.cell(row=row, column=col).value
        return sheet.cell(row=row, column=col).value

    def get_row_value(self, sheet, row):
        '''返回某一行的值'''
        try:
            col = self.get_col_num(sheet)
            data = []
            for i in range(1, col+1):
                data.append(self.get_cell_value(sheet, row, i))
            return data
        except Exception as e:
            raise e

    def write_cell(self, sheet, row, col, filename, content):
        '''单元格赋值'''
        try:
            # self.sheet.cell(row=row, column=col, value=content)
            sheet.cell(row=row, column=col, value=content)
            self.workbook.save(filename)

        except Exception as e:
            raise e


if __name__ == '__main__':
    execl = ExcelParse()
    execl.load_workbook(Test_Data_Path + '/test_data.xlsx')
    sheet = execl.get_sheet('case')
    # execl.get_sheet('login')
    res = execl.get_row_value(sheet, 2)
    print(res)

Util文件夹的elementAction.py文件:执行动作的封装

# elementAction.py
import time

from selenium import webdriver
from Util.find_ele import find_element, find_elements

driver = None

def open_browse(browser_name, *args):
    '''打开浏览器'''
    global driver
    try:
        if browser_name.lower() == 'chrome':
            driver = webdriver.Chrome()
        elif browser_name.lower() == 'firefox':
            driver = webdriver.Firefox()
        else:
            driver = webdriver.Ie()
    except Exception as e:
        raise e

def get_url(url, *args):
    '''打开网址'''
    try:
        driver.get(url)
    except Exception as e:
        raise e


def max_window(*args):
    '''窗口最大化'''
    try:
        driver.maximize_window()
    except Exception as e:
        raise e

def switch_frame(location_type, location_express, *args):
    '''切换iframe'''
    try:
        frame = find_element(driver, location_type, location_express)
        driver.switch_to.frame(frame)
    except Exception as e:
        raise e

def input_content(location_type, location_express, content, *args):
    '''定位输入框,输入内容'''
    try:
        find_element(driver, location_type, location_express).send_keys(content)
    except Exception as e:
        raise e

def input_subject(location_type, location_express, input_conetnt, *args):
    '''定位输入框,输入内容'''
    try:
        # location_express的值为:
        location_express, index = location_express.split(',')
        find_elements(driver, location_type, location_express)[int(index)].send_keys(input_conetnt)
    except Exception as e:
        raise e


def switch_default(*args):
    '''返回默认iframe'''
    try:
        driver.switch_to.default_content()
    except Exception as e:
        raise e


def click(location_type, location_express, *args):
    '''点击操作'''
    try:
        find_element(driver, location_type, location_express).click()
    except Exception as e:
        raise e

def assert_title(title, *args):
    '''断言title是否正确'''
    try:
        assert title in driver.title
    except Exception as e:
        raise e


def close_browse():
    '''关闭浏览器'''
    driver.quit()


def sleep(sec):
    '''等待'''
    time.sleep(sec)

if __name__ == '__main__':
    open_browse('chrome')
    get_url('http://mail.163.com')
    max_window()
    switch_frame('tag name', 'iframe')
    input_content('name', 'email', 'YM_yimin')
    input_content('name', 'password', 'yimin19960930')
    click('id', 'dologin')
    assert_title('网易')

Util文件夹的common.py文件:封装拼接的执行动作函数

# common.py
def generate_method_express(location_type, location_express, key_word, operate_data):
    # location_type, location_express为空,operate_data不为空
    if key_word and operate_data and location_type is None and location_express is None:
        # 判断操作值的类型
        if isinstance(operate_data, int):
            method_express = key_word + '(' + str(operate_data) + ')'
        else:
            method_express = key_word + "('" + operate_data + "')"
        # print(method_express)
    # 只有关键字有值,其他的都为空,比如:max_window, close_browse
    elif key_word and operate_data is None and location_type is None and location_express is None:
        method_express = key_word + '()'
        # print(method_express)
    # location_type,location_express不为空,operate_data为空
    elif key_word and location_type and location_express and operate_data is None:
        method_express = key_word + "('" + location_type + "','" + location_express + "')"
        # print(method_express)
    # 都不为空
    else:
        if isinstance(operate_data, int):
            method_express = key_word + "('" + location_type + "','" + location_express + "'," + str(operate_data) + ")"
        else:
            method_express = key_word + "('" + location_type + "','" + location_express + "','" + operate_data + "')"
        print(method_express)
    return method_express

TestScript文件夹下的add_contractor.py文件:添加联系人的测试用例执行

# 添加联系人
import time

from Util.common import generate_method_express
from Util.excel_parse import ExcelParse
from Setting.Config import Test_Data_Path
from Util.elementAction import *
from Util import elementAction
from Util.find_ele import find_element


def add_contractors(excel, stepSheet, dataSheet):
    '''添加联系人'''
    # 数据源行数
    data_row_nums = excel.get_row_num(dataSheet)
    # 步骤行数
    step_row_nums = excel.get_row_num(stepSheet)
    # 成功的步骤数
    success_record = 0
    # 数据驱动sheet页中需要执行的行数
    need_run_record = 0
    # 遍历数据驱动sheet页中的数据
    for i in range(2, data_row_nums):
        # 判断数据驱动sheet页的数据是否需要执行
        if excel.get_cell_value(dataSheet, i, 6).lower() == 'y':
            need_run_record += 1
            # 将这一行的数据全部拿出来
            name = excel.get_cell_value(dataSheet, i, 1)   # 姓名
            email = excel.get_cell_value(dataSheet, i, 2)   # 邮箱
            is_star = excel.get_cell_value(dataSheet, i, 3)   # 是否星标
            phone = excel.get_cell_value(dataSheet, i, 4)    # 电话号码
            remarks = excel.get_cell_value(dataSheet, i, 2)   # 备注

            success_step = 0     # 记录每行数据成功的步骤数
            # 编辑关键字驱动sheet页中的步骤
            for j in range(2, step_row_nums):
                # 获取关键字驱动sheet页中的每行数据
                step_desc = excel.get_cell_value(stepSheet, j, 2)         # 步骤描述
                location_type = excel.get_cell_value(stepSheet, j, 3)     # 定位方式
                location_express = excel.get_cell_value(stepSheet, j, 4)   # 定位方式表达式
                keyword = excel.get_cell_value(stepSheet, j, 5)           # 关键字
                operate_value = excel.get_cell_value(stepSheet, j, 6)      # 操作值

                # 当操作值是变量的时候,要引用数据源(数据驱动sheet页)中的数据
                # operate_value的值是字符串,并且以${开头,}结尾, 例如operate_value='${name}'
                if isinstance(operate_value, str) and operate_value.startswith('${') and operate_value.endswith('}'):
                    # 把operate_value中的变量名截取出来
                    operate_value = eval(operate_value[2:operate_value.index('}')])
                # 组装函数,拼接每个步骤的执行动作函数
                func_express = generate_method_express(location_type, location_express, keyword, operate_value)

                # 当step_desc为星标是否选择时,当operate_value等于Y(即执行点击操作),选中星标,当operate_value等于Y,不选中星标(即不执行点击操作)
                # func_express = click(location_type, location_express, 'Y/N')
                if operate_value != 'no_star':
                    # 执行选中星标,点击操作
                    try:
                        eval(func_express)
                    except Exception as e:
                        raise e
                    else:
                        # 执行选中星标操作,没有异常,成功步骤+1
                        success_step += 1
                else:
                    # 不执行选中星标操作,要记录成功步骤数
                    success_step += 1
            # 判断成功步骤数,与关键字驱动sheet页的步骤数是否相等
            if success_step+1 == step_row_nums:
                # 成功步骤数+1 等于 关键字驱动sheet页的行数, 成功的数据+1
                success_record += 1
                # 将成功的结果写入数据驱动sheet页对应的单元格
                excel.write_cell(dataSheet, i, 7, Test_Data_Path+'/test_data.xlsx', 'pass')
            else:
                excel.write_cell(dataSheet, i, 7, Test_Data_Path + '/test_data.xlsx', 'fail')
    # 数据驱动sheet页中的所有数据全部轮训执行完之后
    # 判断成功记录数success_record 和 需要执行的数据need_run_record  相等,则说明该测试用例执行成功
    if success_record == need_run_record:
        return 'Pass'
    else:
        return 'Fail'


if __name__ == '__main__':
    from selenium import webdriver
    driver = webdriver.Chrome()
    driver.get('http://mail.163.com')
    frame = find_element(driver, 'tag name', 'iframe')
    driver.switch_to.frame(frame)
    find_element(driver, 'name', 'email').send_keys('test123')
    find_element(driver, 'name', 'password').send_keys('a123456')
    find_element(driver, 'id', 'dologin').click()
    time.sleep(5)

    elementAction.driver = driver

    execl = ExcelParse()
    execl.load_workbook(Test_Data_Path + '/test_data.xlsx')
    step_sheet = execl.get_sheet('add_person_step')
    data_sheet = execl.get_sheet('add_person_data')
    add_contractors(execl,step_sheet, data_sheet)

TestScript文件夹的test_login_add_send.py文件:读取case页的测试用例,进行执行

# test_login_add_send.py
from Util.excel_parse import ExcelParse
from Setting.Config import Test_Data_Path
from TestScript.add_contractor import add_contractors
from Util.common import generate_method_express

def test_loginAndAddAndSend():
    try:
        # 获取数据文件case中的内容
        excel = ExcelParse()
        excel.load_workbook(Test_Data_Path + '/test_data.xlsx')
        case_sheet = excel.get_sheet('case')    # 获取测试用例sheet页
        case_nums = excel.get_row_num(case_sheet)     # case的总行数
        # 遍历case中的数据
        for i in range(2, case_nums+1):
            # 判断该用例是否要执行
            if excel.get_cell_value(case_sheet, i, 7) == 'y':
                # 获取用例名称
                case_name = excel.get_cell_value(case_sheet, i, 2)
                # 框架类型
                frame_mode = excel.get_cell_value(case_sheet, i, 4)
                # 步骤sheet名
                step_sheet_name = excel.get_cell_value(case_sheet, i, 5)
                stepsheet = excel.get_sheet(step_sheet_name)   # 获取步骤sheet页
                if frame_mode == 'data':
                    # 如果框架类型为data,获取数据sheet名
                    data_sheet_name = excel.get_cell_value(case_sheet, i, 6)
                    # 分别获取两个sheet,作为参数传入
                    datasheet = excel.get_sheet(data_sheet_name)
                    result = None
                    # 调用对应的方法,即添加联系人的方法
                    if case_name == 'add_person':
                        result = add_contractors(excel, stepsheet, datasheet)
                        if result == 'Pass':
                            excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Pass')
                        else:
                            excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Fail')
                elif frame_mode == 'key':
                    # 获取步骤数
                    step_nums = excel.get_row_num(stepsheet)
                    # 记录成功的步骤数
                    success_step_num = 0
                    for j in range(2, step_nums+1):
                        # 步骤描述
                        step_desc = excel.get_cell_value(stepsheet, j, 2)
                        location_type = step_desc = excel.get_cell_value(stepsheet, j, 3)
                        location_express = step_desc = excel.get_cell_value(stepsheet, j, 4)
                        key_word = excel.get_cell_value(stepsheet, j, 5)
                        operate_value = step_desc = excel.get_cell_value(stepsheet, j, 6)
                        # 构建函数表达式
                        func_express = generate_method_express(location_type, location_express, key_word, operate_value)

                        # 执行函数, 不抛出异常就认为执行成功
                        try:
                            print(f'开始执行 {step_desc}')
                            eval(func_express)
                        except Exception as e:
                            print(f'执行{step_desc}发生异常{e}')
                            # 某一步发生异常,则该用例执行失败,将失败结果写入测试用例(case)sheet页
                            excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Fail')
                        else:
                            success_step_num += 1
                    # 执行成功步骤数+1 = 步骤总数,用例执行成功
                    if success_step_num+1 == step_nums:
                        # 写入成功
                        excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Pass')
            else:
                excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Skip')

    except Exception as e:
        raise e

最后执行test_login_add_send.py文件,即实现了混合框架。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/969990.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

官方发布:Mac 版 Visual Studio IDE将于明年 8 月 31 日停止支持

近日,微软官方宣布:适用于 Mac 平台的 Visual Studio 集成开发环境(IDE)已经启动 "退休" 进程。Visual Studio for Mac 17.6 将继续支持 12 个月,持续到 2024 年 8 月 31 日。 微软表示在未来的 1 年内将重…

Matlab图像处理-中值滤波

中值滤波 中值滤波也是基于空间域(邻域)操作的,中值滤波是将像素邻域内灰度的中值来代替中心像素的值,把不同灰度的像素点看起来更接近于邻域内的像素点。 优点是可以很好的过滤掉椒盐噪声。中值滤波是非线性的图像处理方法&…

Matlab图像处理-均值滤波

均值滤波 均值滤波所使用的运算是卷积。均值滤波用邻域内像素的平均值来代替中心像素的值,相当于低通滤波,有将图像模糊化的趋势,对椒盐噪声基本无能为力。 在MATLAB中,可使用imfilter函数来实现线性空间滤波,该函数的…

被动操作系统指纹识别的强大功能可实现准确的物联网设备识别

到 2030 年,企业网络和互联网上的物联网设备数量预计将达到290 亿。这种指数级增长无意中增加了攻击面。 每个互连设备都可能为网络攻击和安全漏洞创造新的途径。Mirai 僵尸网络通过使用数千个易受攻击的 IoT 设备对关键互联网基础设施和热门网站发起大规模 DDoS 攻…

查看占用GPU资源的 进程所属docker容器,并杀死 docker 中的僵尸进程!

查看占用GPU资源的所属docker 进程,并杀死 docker 中的僵尸进程! 问题描述:查看当前占用GPU资源的进程属于哪个Docker容器杀死 docker 中的僵尸进程 问题描述: 问题1:一台服务器,每个人在上面 run 一个容器…

企业主流全链路监控系统 - OpenTelemetry(二)

OpenTelemetry 二 4. 部署(python)准备工作(1/5)创建 HTTP Server(2/5)Automatic instrumentation(3/5)增加观测项(Manual)(4/5)向 Co…

win11右键菜单栏改回win10

1.右键 WIN 图标,点击 " 终端(管理员) ” 2.执行以下命令 reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve3.重启电脑

【c++随笔03】构造函数、析构函数、拷贝构造函数、移动构造函数

【c随笔03】构造函数、析构函数、拷贝构造函数、移动构造函数 一、构造函数1、为何要有构造函数?2、构造函数定义3、无参构造、带参构造4、构造函数注意事项4.1 构造函数是特殊的,不是常规的成员函数,不能直接调d1.Date() 。4.2 如果通过无参…

R语言数据管理

1.将变量加入列表中 > mydata<-data.frame(x1c(2,3,4,5),x2c(2,5,7,9)) > mydatax1 x2 1 2 2 2 3 5 3 4 7 4 5 9 > sumx<-x1x2 Error: object x1 not found > sumx<-mydata$x1mydata$x2 > sumx [1] 4 8 11 14 > ls() [1] "mydata&…

【Java 基础篇】StringBuilder的魔力:Java字符串处理探究

在Java编程中&#xff0c;字符串是一个常见的数据类型&#xff0c;用于存储文本信息。然而&#xff0c;与字符串相关的操作可能会导致性能问题&#xff0c;因为字符串是不可变的&#xff0c;每次对字符串进行操作都会创建一个新的字符串对象。为了解决这个问题&#xff0c;Java…

RT-Thread I/O设备模型(一)

I/O设备模型 绝大部分的嵌入式系统都包括一些I/O&#xff08;Input/Output&#xff0c;输入/输出&#xff09;设备&#xff0c;例如仪器上的数据显示屏&#xff0c;工业设备上的串口通信、数据采集设备上用于保存数据的 Flash 或 SD 卡&#xff0c;以及网络设备的以太网接口等…

docker 笔记11: Docker容器监控之CAdvisor+InfluxDB+Granfana

1.原生命令 docker stats命令的结果 是什么 2.是什么 容器监控3剑客 CAdvisor监控收集InfluxDB存储数据Granfana展示图表 3.CAdvisor 4.InfluxDB 5.Granfana 6.总结 7.compose容器编排&#xff0c;一套带走 新建目录 7.1新建3件套组合的 docker-compose.yml version: 3.1vo…

如何写出一篇优秀的博客

写一篇优秀的博客需要经过以下几个步骤&#xff1a; 确定博客的主题和目的 首先要确定博客的主题和目的。这可以根据自己的专业领域、兴趣爱好或者行业热点来定。博客的主题应该具有一定的深度&#xff0c;能够吸引读者的关注&#xff0c;同时博客的目的应该明确&#xff0c;是…

基于x86_64 ubuntu22.04的framebuffer编程

文章目录 前言一、framebuffer简介二、framebuffer接口1.framebuffer设备描述信息2.framebuffer访问接口3.查询/设置可更改信息 三、使用步骤 前言 前段时间由于笔记本没有保管好&#xff0c;LCD显示屏压碎了。于是&#xff0c;将笔记本电脑拆开查看LCD型号。在淘宝上下单买了…

【Java 基础篇】玩转 Java String:技巧与实践

在Java编程中&#xff0c;字符串&#xff08;String&#xff09;是一个非常常见的数据类型&#xff0c;用于存储文本信息。无论是处理用户输入、读取文件内容还是与外部系统进行通信&#xff0c;字符串都扮演着重要的角色。本篇博客将深入讨论Java中的字符串&#xff08;String…

双边滤波 Bilateral Filtering

本文是对图像去噪领域经典的双边滤波法的一个简要介绍与总结&#xff0c;论文链接如下&#xff1a; https://users.soe.ucsc.edu/~manduchi/Papers/ICCV98.pdf 1.前言引入 对一副原始灰度图像&#xff0c;我们将它建模为一张二维矩阵u&#xff0c;每个元素称为一个像素pixel&am…

PID串行多闭环控制与并行多闭环控制的优缺点分析和应用比较

导言&#xff1a; 在自动控制领域&#xff0c;PID控制器是一种经典的控制策略&#xff0c;被广泛应用于各种工业和非工业过程。随着控制系统的复杂性增加&#xff0c;PID串行多闭环控制和PID并行多闭环控制成为解决复杂控制问题的重要方法。本文将从优点和缺点的角度对这两种控…

大数据Flink(七十二):SQL窗口的概述和Over Windows

文章目录 SQL窗口的概述和Over Windows 一、窗口的概述

【100天精通Python】Day53:Python 数据分析_NumPy数据操作和分析进阶

目录 1. 广播 2 文件输入和输出 3 随机数生成 4 线性代数操作 5 进阶操作 6 数据分析示例 1. 广播 广播是NumPy中的一种机制&#xff0c;用于在不同形状的数组之间执行元素级操作&#xff0c;使它们具有兼容的形状。广播允许你在不显式复制数据的情况下&#xff0c;对不同…

2022年09月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;二叉树的深度 给定一棵二叉树&#xff0c;求该二叉树的深度 二叉树深度定义&#xff1a;从根结点到叶结点依次经过的结点&#xff08;含根、叶结点&#xff09;形成树的一条路径&#xff0c;最长路径的…