UI 自动化测试框架:关键字驱动+数据驱动

本文介绍了UI自动化测试的关键字驱动框架,包括其原理、优势和工程结构。关键字驱动测试将测试逻辑封装在数据文件中,简化了测试脚本编写,提高了代码重用性。框架分为四层,包括测试工具层、服务层、测试用例逻辑层和测试场景层,实现了数据驱动和关键字驱动的结合,便于测试人员实施自动化测试。

1. 关键字驱动框架简介

原理及特点

  1. 关键字驱动测试是数据驱动测试的一种改进类型,它也被称为表格驱动测试或者基于动作字的测试。
  2. 主要关键字包括三类:被操作对象(Item)、操作行为(Operation)和操作值(Value),用面向对象形式可将其表现为 Item.Operation(Value)
  3. 将测试逻辑按照这些关键字进行分解,形成数据文件。
  4. 用关键字的形式将测试逻辑封装在数据文件中,测试工具只要能够解释这些关键字即可对其应用自动化。

优势

  1. 执行人员可以不需要太多的技术:一旦框架建立,手工测试人员和非技术人员都可以很容易的编写自动化测试脚本。
  2. 简单易懂:它存在Excel表格中,没有编码,测试脚本容易阅读和理解。关键字和操作行为这样的手工测试用例,使它变得更容易编写和维护。
  3. 早期介入:可以在应用未提交测试之前,就可以建立关键字驱动测试用例对象库,从而减少后期工作。使用需求和其它相关文档进行收集信息,关键字数据表可以建立手工测试程序。
  4. 代码的重用性:用关键字的形式将测试用例及数据进行组装并解释执行,提高代码的可重用性。

 

2. 工程结构说明

工程结构

整个测试框架分为四层,通过分层的方式,测试代码更容易理解,维护起来较为方便。

第一层是“测试工具层”:

  • util 包:用于实现测试过程中调用的工具类方法,例如读取配置文件、页面元素的操作方法、操作 Excel 文件、生成测试报告、发送邮件等。
  • conf 包:配置文件及全局变量。
  • log 目录:日志输出文件。
  • exception_pic 目录:失败用例的截图保存目录。

第二层是“服务层”:相当于对测试对象的一个业务封装。对于接口测试,是对远程方法的一个实现;对于 UI 测试,是对页面元素或操作的一个封装

  • action 包:封装具体的页面动作,如点击、输入文本等。

第三层是“测试用例逻辑层”:该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验

  • bussiness_process 包:基于关键字的形式,实现单条、多条用例的测试脚本逻辑。
  • test_data 目录:Excel 数据文件,包含用例步骤、被操作对象、操作动作、操作值、测试结果等。

第四层是“测试场景层”:将测试用例组织成测试场景,实现各种级别 cases 的管理,如冒烟,回归等测试场景

  • main.py:本框架工程的运行主入口。

框架特点

  1. 基于关键字测试框架,即使不懂开发技术的测试人员也可以实施自动化测试,便于在整个测试团队中推广和使用自动化测试技术,降低自动化测试实施的技术门槛。
  2. 使用外部测试数据文件,使用Excel管理测试用例的集合和每个测试用例的所有执行步骤,实现在一个文件中完成测试用例的维护工作。
  3. 通过定义关键字、操作元素的定位方式和定位表达式和操作值,就可以实现每个测试步骤的执行,可以更加灵活地实现自动化测试的需求。
  4. 基于关键字的方式,可以进行任意关键字的扩展,以满足更加复杂的自动化测试需求。
  5. 实现定位表达式和测试代码的分离,实现定位表达式直接在数据文件中进行维护。
  6. 框架提供日志功能,方便调试和监控自动化测试程序的执行。

 

3. 工程代码实现

action 包

action 包为框架第二层“服务层”,相当于对测试对象的一个业务封装。对于接口测试,是对远程方法的一个实现;对于 UI 测试,是对页面元素或操作的一个封装。

page_action.py

该模块基于关键字格式,封装了页面操作的常用函数,如打开浏览器、点击、输入文本等。

  1 from selenium import webdriver
  2 import time
  3 import traceback
  4 from util.datetime_util import *
  5 from util.find_element_util import *
  6 from util.ini_parser import *
  7 from util.log_util import *
  8 
  9 
 10 DRIVER = ""
 11 
 12 
 13 # 初始化浏览器
 14 def init_browser(browser_name):
 15     global DRIVER
 16     if browser_name.lower() == "chrome":
 17         DRIVER = webdriver.Chrome(CHROME_DRIVER)
 18     elif browser_name.lower() == "firefox":
 19         DRIVER = webdriver.Firefox(FIREFOX_DRIVER)
 20     elif browser_name.lower() == "ie":
 21         DRIVER = webdriver.Ie(IE_DRIVER)
 22     else:
 23         warning("浏览器【%s】不支持,已默认启动chrome" % browser_name)
 24         DRIVER = webdriver.Chrome(CHROME_DRIVER)
 25 
 26 
 27 # 访问指定url
 28 def visit(url):
 29     global DRIVER
 30     DRIVER.get(url)
 31 
 32 
 33 # 输入操作
 34 def input(locate_method, locate_exp, value):
 35     global DRIVER
 36     # 方式1:直接传定位方式和定位表达式
 37     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 38                              "partial link text", "css selector"]:
 39         find_element(DRIVER, locate_method, locate_exp).send_keys(value)
 40     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 41     else:
 42         parser = IniParser(ELEMENT_FILE_PATH)
 43         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 44         find_element(DRIVER, locate_method, locate_exp).send_keys(value)
 45 
 46 
 47 # 点击操作
 48 def click(locate_method, locate_exp):
 49     global DRIVER
 50     # 方式1:直接传定位方式和定位表达式
 51     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 52                              "partial link text", "css selector"]:
 53         find_element(DRIVER, locate_method, locate_exp).click()
 54     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 55     else:
 56         parser = IniParser(ELEMENT_FILE_PATH)
 57         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 58         find_element(DRIVER, locate_method, locate_exp).click()
 59 
 60 
 61 # 清空输入框操作
 62 def clear(locate_method, locate_exp):
 63     global DRIVER
 64     # 方式1:直接传定位方式和定位表达式
 65     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 66                              "partial link text", "css selector"]:
 67         find_element(DRIVER, locate_method, locate_exp).clear()
 68     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 69     else:
 70         parser = IniParser(ELEMENT_FILE_PATH)
 71         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 72         find_element(DRIVER, locate_method, locate_exp).clear()
 73 
 74 
 75 # 切换frame
 76 def switch_frame(locate_method, locate_exp):
 77     global DRIVER
 78     # 方式1:直接传定位方式和定位表达式
 79     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 80                              "partial link text", "css selector"]:
 81         DRIVER.switch_to.frame(find_element(DRIVER, locate_method, locate_exp))
 82     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 83     else:
 84         parser = IniParser(ELEMENT_FILE_PATH)
 85         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 86         DRIVER.switch_to.frame(find_element(DRIVER, locate_method, locate_exp))
 87 
 88 
 89 # 切换主frame
 90 def switch_home_frame():
 91     global DRIVER
 92     DRIVER.switch_to.default_content()
 93 
 94 
 95 # 断言
 96 def assert_word(keyword):
 97     global DRIVER
 98     assert keyword in DRIVER.page_source
 99 
100 
101 # 休眠
102 def sleep(times):
103     time.sleep(int(times))
104 
105 
106 # 关闭浏览器
107 def quit():
108     global DRIVER
109     DRIVER.quit()
110 
111 
112 # 截图函数
113 def take_screenshot():
114     global DRIVER
115     # 创建当前日期目录
116     dir = os.path.join(SCREENSHOT_PATH, get_chinese_date())
117     if not os.path.exists(dir):
118         os.makedirs(dir)
119     # 以当前时间为文件名
120     file_name = get_chinese_time()
121     file_path = os.path.join(dir, file_name+".png")
122     try:
123         DRIVER.get_screenshot_as_file(file_path)
124         # 返回截图文件的绝对路径
125         return file_path
126     except:
127         error("截图发生异常【{}】\n{}".format(file_path, traceback.format_exc()))
128         return file_path
129 
130 
131 if __name__ == "__main__":
132     init_browser("chrome")
133     visit("http://mail.126.com/")
134     print(take_screenshot())

 

business_process 包

business_process 包是框架第三层“测试用例逻辑层”,该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验。

case_process.py

  • 测试用例文件的一行数据,拼接其中的操作动作、操作对象、操作值等关键字,形成与 page_action.py 中的函数相对应的字符串,并通过 eval() 转成表达式以执行用例。
  • 记录该用例的测试结果,如测试执行结果、测试执行时间等。
  • 如需数据驱动的用例集,则获取数据驱动的数据源集合,循环将每组数据传递给用例步骤。
  • 如果遇到需要参数化的值 ${变量名},则根据数据驱动的数据源,根据变量名进行参数化。

 1 import traceback
 2 import re
 3 from util.global_var import *
 4 from util.log_util import *
 5 from util.datetime_util import *
 6 from util.excel_util import Excel
 7 from action.page_action import *
 8 
 9 
10 # 执行一条测试用例(即一行测试数据)
11 def execute_case(excel_file_path, case_data, test_data_source=None):
12     # 用例数据格式校验
13     if not isinstance(case_data, (list, tuple)):
14         error("测试用例数据格式有误!测试数据应为列表或元组类型!【%s
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值