前端自动化测试框架

项目说明

  • 本框架是一套基于selenium+Python3.7+yaml+Robot Framework而设计的数据驱动UI自动化测试框架,Robot Framework 作为执行器,本框架整体特点为:用例与代码分离;用例驱动测试的执行;分层设计、脚本模块化;自动准备测试条件和数据、事后清理;支持失败多次重试、且在每次重试前都会还原一次测试环境,保证重试的成功率;支持邮件呈现测试结果;可灵活挑选项目和用例执行。

框架特点说明

  • 数据驱动
  1. 封装一个测试脚本,通过不同的数据来驱动,保证测试覆盖率
  2. 用例执行前初始化,执行后释放资源
  3. 执行测试数据,并进行结果判断,测试失败或者异常时,会产生相关日志和截图
  • 用例与代码分离
  1. 代码中不含有测试用例
  2. 测试用例采用robot格式撰写
  3. 用例按功能点分类
  4. 每个用例都是独立的,不互相依赖也不互相影响
  5. 可按项目、模块、测试点挑选执行用例
  6. 运行结束后,还原测试环境,清理脏数据
  • 分层设计
  1. 操作对象、页面元素、业务逻辑、测试数据相互剥离,灵活调用
  2. 封装底层操作组件
  3. 按页面封装和分类页面元素
  4. 封装基础逻辑,组合业务逻辑实现功能点
  • 脚本模块化
  1. 不同功能脚本模块化,各模块间保持独立性和可融合性
  2. 封装基础方法,如随机邮箱等
  3. 封装通用模块,如文件读写、压缩文件、邮件发送、日志等
  4. 模块支持自主开启和关闭
  • 失败多次重试
  1. 可自主开启和关闭失败自动重试功能
  2. 重试开始在每轮测试结束之后
  3. 每次重试前后会初始化环境和还原环境
  4. 只重试失败用例
  5. 每次重试结束会自动合并重试报告
  6. 重试结束会自动合并出总报告
  7. 报告可选择自动压缩并邮件发送
  8. 测试结束后,也可手动启动失败重试和合并报告

技术栈

  • selenium
  • Python3.7
  • Robot Framework
  • yaml
  • AutoItLibrary
  • logging
  • SSHLibrary
  • databaselibrary

环境部署

  • 安装python3.7,并将Python的安装目录添加到系统环境变量的Path路径中
  • 命令行窗口执行pip install -r requirements.txt 安装工程所依赖的库文件
  • AutoItLibrary安装
  1. 安装autoit-v3-setup.exe(V3.3.14.5),安装过程中一定要选x64,不然加载AutoItLibrary还是会是红色
  2. pip install robotframework-autoitlibrary
  3. 在Python安装目录(\Python37\Lib\site-packages\win32com\client)下,修改dynamic.py文件,在import pythoncom后,加 pythoncom.CoInitialize() 。(目的是解决在ride中导入AutoItLibrary时,报(-2147221008, '尚未调用 CoInitialize。', None, None)的错误。)
  • 将相关浏览器的驱动,比如谷歌浏览器的驱动chromedriver.exe放在python的安装目录(\Python37)下
  • 将自定义库,比如randomGenerator.py放在python目录(\Python37\Lib\site-packages)下
  • 在Python安装目录(\Python37\Lib\site-packages\robotide\contrib\testrunner)下,修改testrunner.py文件,将'latin1' if IS_WINDOWS修改为'mbcs' if IS_WINDOWS。(目的是解决RIDE控制台中文显示乱码的问题),修改后的内容和位置具体如下:

for _ in myqueuerng:
try:
# DEBUG result += self._queue.get_nowait()
# .decode(utils.SYSTEM_ENCODING, 'replace')
# .decode('UTF-8','ignore')
result += encoding.console_decode(self._queue.get_nowait(),
'mbcs' if IS_WINDOWS
else 'UTF-8')

安装UI元素定位工具(根据实际需要选装)

  • 火狐浏览器插件
  1. Try XPath
  2. xPath Finder
  3. ChroPath
  • 谷歌浏览器插件
  1. ChroPath

框架目录结构图及相关说明

1、代码目录结构图如下

2、目录结构说明

  • Config ===========> 配置文件
  • pycode ===========> python公共方法、模块封装,工具类等
  • rfcode ==========> 存放项目/系统的测试用例、公共配置、界面元素、业务逻辑操作等等
  • logs ==========> 日志文件
  • Report ==========> 测试报告
  • common_Run.py ===========> 测试用例总执行模块
  • testfile ============> 存放测试过程的操作文件
  • requirements.txt ============> 相关依赖包文件
  • run_test.bat =============> 测试启动按钮,测试过程中,若有失败则自动进行失败重试
  • retry.bat ============> 测试结束后,再次进行失败重试手动启动按钮

代码设计与功能说明

1、定义运行配置文件 config.yml

该文件主要控制测试的执行方式、模块的功能开关、测试用例的筛选、邮件的配置以及日志的配置,具体如下:


config.yml配置信息

---
#待执行的产品项目, Cloud -云项目; rfcode -执行所有的项目
ProductItem: Cloud #待执行的测试套件(标签), all -所有的测试套件: debug -调试中套件; login -登录功能 ; filter -账号筛选: P1 -一级用例
testSuite: P1 #测试报告标题
testReportTitle: Cloud_Report #失败重试开关, 0 -关, 1 -开 ,开启后默认重试一次
retry_switch: 0
#失败重试次数
retryTime: 1 #发送测试报告邮件开关, 0 -关, 1 -开
emailSwitch: 1 #邮件配置
#发件邮箱
smtp_server: smtp.126.com
server_username: XXXtest@126.com
server_pwd: XXXXXX
#收件人(列表)
msg_to:
- XXX1@qq.com
- XXX2@126.com #邮件主题
msg_subject: '[XX项目][测试环境-develop][jira号][自动化测试报告]' #日志级别(字典),由高到低: CRITICAL 、 ERROR 、 WARNING 、 INFO 、 DEBUG
log:
file_name: test.log
backup: 5
console_level: DEBUG #控制台日志级别
file_level: DEBUG #文件日志级别
pattern: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

2、测试用例的设计

测试用例以robot格式的文件保存,平时只需维护测试数据和期望结果,维护成本低。测试用例的数据格式如下:

3、用例执行脚本

按照测试用例,组合业务逻辑,实现功能点测试,以供测试用例循环调用

4、测试执行主程序common_Run.py(收集测试用例,批量执行并生成测试报告,并发送报告到邮件)


# coding: utf-8

import os,sys
import time
from config import BASE_PATH,Config
from global_model.log import logger
from global_model.format_change import formatChange
from email_model.run_sendEmail import sendEailMock def batch_Call(basePath, product_item, testCase_tag):
'''
首次执行测试:
param robot_testSuite: robot testSuite路径;
param product_item: 执行的待测产品项目;
param testCase_tag: 执行的测试用例标签;
param testCaseReportPath: 测试报告路径;
'''
try:
logger.info(u'=======================首次测试执行开始!正在初始化数据库!=============================')
pybot_result_list = []
output_dir = "-d " + basePath + "/Report/primo_report"
output_xml = "-o output.xml"
output_log = "-l log.html"
output_report = "-r report.html"
productItem = "-s " + product_item
caseTag = "-i " + testCase_tag
pybot_cmd = "pybot " + output_dir + " " + output_xml + " " + output_log + " " + output_report + " " + productItem + " " + caseTag + " " + basePath
pybot_result=os.system(pybot_cmd)
return pybot_result except Exception as e:
logger.exception(u'首次执行测试失败. %s', e) finally:
logger.info(u'测试项目根目录为:{0}'.format(basePath))
logger.debug(u'首次执行测试报告输出目录:{0}'.format(output_dir))
logger.info(u'首次执行测试的pybot命令:{0}'.format(pybot_cmd))
logger.info(u'======================首次执行测试已完成!用例失败数为:{0} 个!========================'.format(pybot_result)) def mergeReport(basePath, report_title, match_report, copy_source_match, copy_target_match, merge_report_output):
'''
合并报告:
param testCaseReportPath: 测试报告路径;
param report_title: 合并报告的标题;
return:
'''
try:
logger.info(u'==========================合并测试报告开始!==================================')
output_dir = "-d " + basePath + "/Report/{0}".format(merge_report_output) # 输出合并报告目录
output_xml = "-o output.xml"
output_log = "-l log.html"
output_report = "-r report.html" # 被合并的报告
merge_report = "--merge " + basePath + "/Report/{0}".format(match_report) + "/output.xml"
title = "--ReportTitle " + report_title
rebot_cmd = r"rebot " + output_dir + " " + output_xml + " " + output_log + " " + output_report + " " + title + " " + merge_report
mergeReport_result = os.system(rebot_cmd)
logger.debug(u'合并报告的结果为:{0}'.format(mergeReport_result)) # 复制截图
testCaseReportPath = basePath + "/Report"
source_files = testCaseReportPath + "/{0}/*.png".format(copy_source_match)
target_dir = testCaseReportPath + "/{0}".format(copy_target_match)
cp_cmd = r"copy " + source_files + " " + target_dir
cp_cmd = cp_cmd.replace("/", "\\")
cope_result = os.system(cp_cmd)
logger.debug(u'复制截图的结果为:{0}'.format(cope_result))
return mergeReport_result except Exception as e:
logger.exception(u'合并报告异常. %s', e) finally:
logger.debug(u'输出合并报告目录为:{0}'.format(output_dir))
logger.info(u'合并报告rebot命令:{0}'.format(rebot_cmd))
if mergeReport_result != 0:
logger.info(u'======================合并报告中有失败用例!========================')
else:
logger.info(u'======================没有失败重试的报告需合并,或者合并报告中的用例已全通过!========================')
logger.info(u'复制截图命令为:{0}'.format(cp_cmd))
if cope_result == 0:
logger.info(u'======================截图已成功复制到合并报告中!========================')
else:
logger.info(u'======================报告中没有需要复制的截图!========================') def cleanLogs(basePath):
'''
删除硬盘中合并前的测试报告:
param testCaseReportPath: 测试报告路径;
return:
'''
try:
logger.info(u'==========================清理磁盘报告开始!==================================')
testCaseReportPath = basePath + "/Report"
primo_report_files = testCaseReportPath + "/primo_report/*"
retry_report_files = testCaseReportPath + "/retry_report/*"
merge_files = testCaseReportPath + "/merge/*"
zip_merges_files = testCaseReportPath + "/zip_merges/*"
retry_times_files = testCaseReportPath + "/retry_times/*"
del_cmd = "del " + primo_report_files + " " + retry_report_files + " " + merge_files + " " + zip_merges_files + " " + retry_times_files
del_cmd = del_cmd.replace("/", "\\")
del_cmd = del_cmd + " " + "/s /q /f"
clean_result = os.system(del_cmd) except Exception as e:
logger.exception(u'清理磁盘报告异常. %s', e) finally:
logger.info(u'清理磁盘报告命令为:{0}'.format(del_cmd))
logger.debug(u'清理磁盘报告结果为:{0}'.format(clean_result))
if clean_result == 0:
logger.info(u'======================测试报告清理成功!========================')
else:
logger.info(u'======================测试报告清理失败!========================') def run_test(basePath, product_item, testCase_tag, report_title, email_switch, smtp_server, server_username, server_pwd, msg_to, msg_subject, retry_switch, retry_time):
'''
失败自动重试:
param robot_testSuite: robot testSuite路径;
param testCase_tag: 执行的测试用例标签;
param testCaseReportPath: 测试报告路径;
param report_title: 合并报告的标题;
'''
#清理环境
cleanLogs(basePath) #首次执行
run_result = batch_Call(basePath, product_item, testCase_tag) #失败重试
retryFunction = retry_switch
if retryFunction == 1:
try:
if run_result != 0:
logger.info(u'======================失败用例重试第 1 次!========================')
output_dir = "-d " + basePath + "/Report/retry_report"
retry_xml = "-R " + basePath + "/Report/primo_report/output.xml"
pybot_cmd = "pybot " + retry_xml + " " + output_dir + " " + basePath
logger.info(u'第 1 次失败重试命令为:{0}'.format(pybot_cmd))
retry_result=os.system(pybot_cmd)
logger.info(u'======================失败用例重试第 1 次已完成!用例失败数为:{0} 个!========================'.format(retry_result)) retryTimes = retry_time
n = 2
while (retryTimes >=2 and retry_result != 0):
logger.info(u'======================失败用例重试第 {0} 次!========================'.format(n))
output_dir = "-d " + basePath + "/Report/retry_times"
retry_xml = "-R " + basePath + "/Report/retry_report/output.xml"
pybot_cmd = "pybot " + retry_xml + " " + output_dir + " " + basePath
logger.info(u'第 {0} 次失败重试命令为:{1}'.format(n, pybot_cmd))
retry_result=os.system(pybot_cmd)
logger.info(u'======================失败用例重试第 {0} 次已完成!用例失败数为:{1} 个!========================'.format(n, retry_result)) logger.info(u'======================正在合并重试报告!========================')
merge_report_output = 'retry_report'
match_report = 'retry_*'
copy_source_match = 'retry_times'
copy_target_match = 'retry_report'
mergeReport(basePath, report_title, match_report, copy_source_match, copy_target_match, merge_report_output)
logger.info(u'======================失败用例重试第 {0} 次合并报告已完成!========================'.format(n))
n = n + 1
retryTimes = retryTimes-1 except Exception as e:
logger.exception(u'失败重试异常. %s', e) else:
logger.info(u'======================本次测试的失败重试功能已关闭!========================') #合并报告
merge_report_output = 'merge'
match_report = '*_report'
copy_source_match = 'retry_report'
copy_target_match = 'merge'
report_result = mergeReport(basePath, report_title, match_report, copy_source_match, copy_target_match, merge_report_output)
logger.debug(u'======================最终测试结果为: {0} !========================'.format(report_result))
if report_result == 0:
ReportResult = u'测试通过!'
else:
ReportResult = u'测试不通过!失败用例数为:{0} 个'.format(report_result) #发送report到邮件
emailFunction = email_switch
if emailFunction == 1: #将字符中的反斜杠转成正斜杠
fileUrl_PATH = basePath.replace('\\','/')
logger.debug(u'基础路径的反斜杠转成正斜杠为:{0}'.format(fileUrl_PATH)) fileUrl='file:///{0}/Report/merge/report.html'.format(fileUrl_PATH)
logger.info(u'html测试报告的url为:{0}'.format(fileUrl))
save_fn=r'{0}\Report\zip_merges\report.png'.format(basePath)
logger.debug(u'转成图片报告后保存的目标路径为:{0}'.format(save_fn))
formatChange_obj = formatChange()
formatChange_obj.html_to_image(fileUrl, save_fn) email_folder_dir = basePath + "/Report/merge" #待压缩文件夹
logger.debug(u'待压缩文件夹为:{0}'.format(email_folder_dir))
email_target_dir = basePath + "/Report/zip_merges" #压缩文件保存路径
sendEailMock_obj = sendEailMock()
sendEailMock_obj.send_email(email_folder_dir, email_target_dir, smtp_server, server_username, server_pwd, msg_to, msg_subject, ReportResult, save_fn)
else:
logger.info(u'======================本次测试的邮件功能已关闭!========================') if __name__ == '__main__': c = Config()
ProductItem = c.get('ProductItem')
testSuite = c.get('testSuite')
testReportTitle = c.get('testReportTitle') emailSwitch = c.get('emailSwitch')
smtp_server = c.get('smtp_server')
server_username = c.get('server_username')
server_pwd = c.get('server_pwd')
msg_to = c.get('msg_to')
msg_subject = c.get('msg_subject') retrySwitch = c.get('retry_switch')
retryTime = c.get('retryTime') run_test(BASE_PATH, ProductItem, testSuite, testReportTitle, emailSwitch, smtp_server, server_username, server_pwd, msg_to, msg_subject, retrySwitch, retryTime)

5、测试报告呈现

  • 测试概要报告

  • 测试执行过程中若有失败重试时,则报告中会呈现出前一次失败的结果和原因,同时也会呈现出重试后成功的结果,并且最终的报告结果以最后一次的重试结果为准

  • 测试执行过程有失败用例时,报告呈现失败截图

  • 报告发送至邮件时,邮件标题呈现测试通过与否,或失败用例数

  • 邮件正文直接显示本次测试概要报告,直观显示出执行用例数,失败用例数,失败的用例模块等结果。

项目实战演示





基于selenium+Python3.7+yaml+Robot Framework的UI自动化测试框架的更多相关文章

  1. 基于Robot Framework的接口自动化测试

    Robot Framework框架简介 Robot Framework框架是一个通用的验收测试和验收测试驱动开发的自动化测试框架(ATDD),使用的是关键字驱动的测试方法.它本身拥有强大的标准库,此外 ...

  2. UI自动化测试框架(项目实战)python、Selenium(日志、邮件、pageobject)

    其实百度UI自动化测试框架,会出来很多相关的信息,不过就没有找到纯项目的,无法拿来使用的:所以我最近就写了一个简单,不过可以拿来在真正项目中可以使用的测试框架. 项目的地址:https://githu ...

  3. 基于Python3 + appium的Ui自动化测试框架

    UiAutoTest 一.概要 数据驱动的Ui自动化框架 二.环境要求 框架基于Python3 + unittest + appium 运行电脑需配置adb.aapt的环境变量,build_tools ...

  4. Selenium+java - 手把手一起搭建一个最简单自动化测试框架

    写在前面 我们刚开始做自动化测试,可能写的代码都是基于原生写的代码,看起来特别不美观,而且感觉特别生硬. 来看下面一段代码,如下图所示: 从上面图片代码来看,具体特征如下: driver对象在测试类中 ...

  5. UI自动化测试框架之Selenium关键字驱动 (转)

    摘要 自动化测试框架demo,用关键字的形式将测试逻辑封装在数据文件中,测试工具解释这些关键字即可对其应用自动化 一.原理及特点 1.   关键字驱动测试是数据驱动测试的一种改进类型 2.    主要 ...

  6. 【转】UI自动化测试框架之Selenium关键字驱动

    原网址:https://my.oschina.net/hellotest/blog/531932#comment-list 摘要: 自动化测试框架demo,用关键字的形式将测试逻辑封装在数据文件中,测 ...

  7. 软件测试自动化之- 基于反射的UI自动化测试框架 - UI Automation Test Framework

    测试自动化程序的任务 基于反射的ui测试自动化程序,要完成的6项任务: 通过某种方式从测试套件程序中运行待测程序(AUT: Applicaton Under Test),以便于两个程序之间进行通信 操 ...

  8. 基于APPIUM测试微信公众号的UI自动化测试框架(结合Allure2测试报告框架)

    框架初衷 前两周组内的小伙伴跟我说她现在测试的微信公众号项目(保险)每次上新产品时测试起来很费时,存在大量的重复操作(点点点),手工测试每个产品可能需要半天到一天的时间,复杂的产品需要两天. 由于保险 ...

  9. UI自动化测试框架之Selenium关键字驱动

    一.原理及特点 1. 关键字驱动测试是数据驱动测试的一种改进类型 2. 主要关键字包括三类:被操作对象(Item).操作(Operation)和值(value),用面向对象形式可将其表现为Item.O ...

随机推荐

  1. solr 对于 关键字的特殊处理

    public static String transformMetachar(String input){      StringBuffer sb = new StringBuffer();     ...

  2. SpringBoot整合WEB开发--(五)自定义错误页

    目的与原理: 处理异常时,若我们想根据实际情况返回不同的页面,@ControllerAdvice与@ExceptionHandler,一般用于处理应用级别的异常,一些容器级别的错误就处理不了,例如Fi ...

  3. sql简单练习语句

    排序是每个软件工程师和开发人员都需要掌握的技能.不仅需要通过编程面试,还要对程序本身有一个全面的理解.不同的排序算法很好地展示了算法设计上如何强烈的影响程序的复杂度.运行速度和效率. 排序有很多种实现 ...

  4. Flatmap 和map 区别

    map将函数作用到数据集的每一个元素上,生成一个新的分布式的数据集(RDD)返回 map函数的源码:   def map(self, f, preservesPartitioning=False): ...

  5. pandas模块详解

    Pandas模块 1.什么是pandas pandas是基于numpy构建的,用来做数据分析的 2.pandas能干什么 具备对其功能的数据结构DataFrame,Series 集成时间序列功能 提供 ...

  6. 使用yaml格式进行接口测试报错

    前言:本人公司使用yaml做接口测试.某日开发写了一个字典嵌套列表,列表里面再嵌套字典的接口. yaml的值应该为下图(注意缩进问题)   加了-代表下面是一个列表 {'uid': '3a61479f ...

  7. EF CodeFirst关于Mysql如何自动生成数据库表

    相对于sqlserver数据库,mysql的配置过程相对麻烦一些,我们从0讲起. 1.新建一个控制台应用程序 右键点击引用--管理NuGet程序包,搜索Mysql.Data.Entity并安装,安装完 ...

  8. Centos6.5安装Nmap、tcpdump、mysql、tomcat、靶场WAVSEP

    nmap安装 输入命令如下: yum install nmap 安装完成后,输入nmap -h看是否安装成功. 安装tcpdump 安装tcpdump必须的库: yum install flex yu ...

  9. C++ 中字符串查找、字符串截取、字符串替换

    参照:C++基础-string截取.替换.查找子串函数 1.字符串查找 s.find(s1) //查找s中第一次出现s1的位置,并返回(包括0) s.rfind(s1) //查找s中最后次出现s1的位 ...

  10. js或者jq的string类型或者number类型的相互转换及json对象与字符串的转换

    1.将值乘以1,将string类型转为number类型 //算合计价值function summoney(money) { var zijin = $("#main_xm_dam09&quo ...