Python3+Selenium2完整的自动化测试实现之旅(七):完整的轻量级自动化框架实现
一、前言
前面系列Python3+Selenium2自动化系列博文,陆陆续续总结了自动化环境最基础环境的搭建、IE和Chrome浏览器驱动配置、selenium下的webdriver模块提供的元素定位和操作鼠标、键盘、警示框、浏览器cookie、多窗口切换等场景的方法、web自动化测试框架、python面向对象和POM设计模型以及python下的单元测试模块unittest模块。
本来计划陆续循序渐进的继续写一些篇章总结python面向对象编程、python下的logging日志模块、os.path路径处理模块、time模块处理时间,如格式化时间输出以及一些第三方模块如读取和向excel文件中写入数据的模块:xlrd和xlwt,写完这些后,再开始写本篇的终极目标:编写一个轻量级的自动化测试框架。
最终放弃这样做,原因:楼主发现python面向对象编程、python的这些标准库模块或者第三方模块其实很多博客已经做了很好的总结,总之,会学习的人,百度后总能从一大堆的文章中,查看并甄选出对自己解决问题或者思考有帮助和收获的文章,如百度python logging模块使用,多看博文就一定能找到对自己有帮助的文章。曾经楼主也是在学习实践中遇到很多坑,也是根据IDE输出上提示的错误自己先思考解决,还是不行就百度一下或者看书,并深度学习下这块的内容,然后再去解决问题,从这些博文以及自己买的python类的书籍中也是受益良多,这也是楼主一直以来自己的学习方式。这里,每个人都有自己的学习和思考问题的方式,找准适合自己的学习方式并执行它,完成一个阶段目标,然后设置下一个新目标,并为之努力。
因此,在写这个轻量级的自动化web测试框架前,我跳过了上述诸多内容,包括且不限于:python面向对象编程、python常用标准库loggging、time、os.path运用等等,在后面的轻量级框架代码中会有部分注释,对于这些python相关的内容学习,大家根据自己的情况去充实,坚持学习并持之以恒。
在楼主身边,有太多类似的人学习总是三天晒鱼、两天打网的,完全沉不下心来学习东西,浅尝辄止,没有积淀,如果认定一个东西就去想办法搞定,加油!楼主也在为自己新的目标fighting中,当然目标是广义的,可以是生活方面、工作方面、情感方面.......,好像跑偏题了O(∩_∩)O,这些人生鸡汤似的废话就不说了,看看下面这个web自动化测试框架是如何实现的吧~
二、项目架构说明
该项目架构基于楼主公司的一款B/S软件设计,大家也可以根据自己的被测软件来构建适合自己的架构层级,当然也可以参考楼主的。做自动化测试项目,当搞懂了思想和方法,其实都是万变不离其宗,就跟写代码一样,语言万千种,唯一不变的就是语言中殊途同归的思想,因此,玩会了套路自然就能凌驾于套路之上,运用并加入自己的东西。在PyCharm中新建如下的项目层级:
有过开发经历的小伙伴都知道,有个好的交互式开发工具对于我们创建和管理清晰的项目架构很方便,PyCharm就是一款交互良好的python开发工具。楼主上面的项目层级中部分目录和目录下的文件没展开,下面显示一个完整的目录结构,并说明每个目录是用来干嘛?放什么东西?
当然这个项目层级设计,不是楼主一时间就固定下来的,也是在不断的摸索和采坑中,不断调整出的一个适合自己的框架目录层级。项目框架设计好了后,接下来就是慢慢补充内容一步步实现上面每个目录需要的东西。来吧,开始造轮子~
三、配置文件设计
首先,对于上面的config.ini的配置文件进行配置。说到配置文件,不管是开发人员还是测试人员都不会陌生,还有xml、txt等格式的配置文件,配置文件就是用来配置一些参数和固定的变量值,一般是程序固定不变的东西我们就放这里面,用于程序直接调用,如果修改配置文件中变量的值,程序调用该变量就会产生不同的输出行为。如在做自动化测试时,我们可以将测试的不同浏览器写入到该文件中,当我们需要调用firefox浏览器时,将参数设置成firefox即可,测试脚本将会在火狐浏览器进行。如下图,在配置文件中设置了浏览器参数、测试url、邮件服务器、邮件发送和接收者等参数。
至于为什么这个ini配置文件需要编辑成【###】然后下面是参数或者变量的赋值,自己百度学习:ini配置文件格式,看下是怎么编辑的?都有哪些要素?
四、日志类模块的实现
说到日志,大家都明白日志的作用,最明显的作用就是在程序的关键位置或者步骤节点下设置日志输出,当程序运行时,会输出日志,日志是我们查看程序运行情况和查找错误的重要手段。因此,对于自动化测试也是如此,我们需要知道自动化执行的情况以及执行错误的情况发生了什么,那就需要给你的自动化测试项目封装一个日志类的功能模块,用于输出日志。python语言封装了一个叫logging的标准库模块,能够设置日志等级以及怎么输出、输出到哪里。对于logging模块,大家可以自己针对性去学习该模块的使用。
我们在上面的项目层级的models目录下创建log.py文件,在这个模块文件下定义一个叫做Logger的日志类,完成日志类的封装,编辑如下代码:
'''Code description:封装浏览器引擎类,读取配置文件实现浏览器类型的选择,并封装打开浏览器和退出的方法Create time:2018-11-12Developer:
'''
# -*- coding: utf-8 -*-
import configparser # python解析配置文件模块
import os.path
from selenium import webdriver
from V2200.test.models.log import Logger # 引入日志类模块logger = Logger(logger="BrowserEngine").getlog() # 实例化对象loggerclass BrowserEngine(object):def __init__(self, driver):self.driver = driver # 初始化构造函数,将参数driver self化便于后面创建的方法直接自动调用def open_browser(self, driver):''':param driver: 读取配置文件,返回driver:return:'''config = configparser.ConfigParser()file_path = os.path.abspath('E:\V2200_AutoTest\V2200\config\config.ini') # 绝对路径写法#print('得到的读取config文件的路径:',file_path)config.read(file_path,encoding='UTF-8') # 读取配置文件browser = config.get("browserType", "browserName")logger.info("选择的浏览器是: %s ." % browser)url = config.get("testServer", "URL")logger.info("测试的平台URL是: %s" % url)if browser == "Firefox":driver = webdriver.Firefox()logger.info("Starting firefox browser.")elif browser == "Chrome":driver = webdriver.Chrome()logger.info("Starting Chrome browser.")elif browser == "Ie":driver = webdriver.Ie()logger.info("Starting IE browser.")driver.get(url) # 得到测试的urllogger.info("浏览器的版本为:%s" % driver.capabilities['version']) # 获取浏览器版本driver.maximize_window()logger.info("最大化浏览器窗口.")driver.implicitly_wait(10)return driverdef quit_browser(self):self.driver.quit()
这样我们就自定义封装了一个简单的日志类模块,设置了日志输出级别、输出格式以及输出日志的位置,后面其他模块需要输出日志时,就可以调用引入该日志类。
五、浏览器模块的实现
日志类实现简单封装后,继续造轮子~。此部分用于封装浏览器模块,主要实现打开和关闭不同浏览器的方法,这里就用到了POM的思想,咱们封装了浏览器的类型和打开关闭方法,那么后面每条测试脚本就可以直接调用打开和关闭浏览器方法,脚本只需要专注具体的测试业务逻辑的实现即可。在models目录下新建broser_engine.py文件,自定义一个叫做BrowserEngine类,实现浏览器模块的封装,代码如下:
'''Code description:封装浏览器引擎类,读取配置文件实现浏览器类型的选择,并封装打开浏览器和退出的方法Create time:2018-11-12Developer:
'''
# -*- coding: utf-8 -*-
import configparser # python解析配置文件模块
import os.path
from selenium import webdriver
from V2200.test.models.log import Logger # 引入日志类模块logger = Logger(logger="BrowserEngine").getlog() # 实例化对象loggerclass BrowserEngine(object):def __init__(self, driver):self.driver = driver # 初始化构造函数,将参数driver self化便于后面创建的方法直接自动调用def open_browser(self, driver):''':param driver: 读取配置文件,返回driver:return:'''config = configparser.ConfigParser()file_path = os.path.abspath('E:\V2200_AutoTest\V2200\config\config.ini') # 绝对路径写法#print('得到的读取config文件的路径:',file_path)config.read(file_path,encoding='UTF-8') # 读取配置文件browser = config.get("browserType", "browserName")logger.info("选择的浏览器是: %s ." % browser)url = config.get("testServer", "URL")logger.info("测试的平台URL是: %s" % url)if browser == "Firefox":driver = webdriver.Firefox()logger.info("Starting firefox browser.")elif browser == "Chrome":driver = webdriver.Chrome()logger.info("Starting Chrome browser.")elif browser == "Ie":driver = webdriver.Ie()logger.info("Starting IE browser.")driver.get(url) # 得到测试的urllogger.info("浏览器的版本为:%s" % driver.capabilities['version']) # 获取浏览器版本driver.maximize_window()logger.info("最大化浏览器窗口.")driver.implicitly_wait(10)return driverdef quit_browser(self):self.driver.quit()
六、页面基类的实现
此部分用于封装页面基类,主要用于封装一些常用的公共方法,如截图方法、元素定位方法、元素通用操作方法、警示框处理方法等等,只要软件页面一些常用的操作都可以写在该页面基类中,这个页面基础类就类似于一个公共函数库一样,封装这些方法,后面有需要的地方直接调用即可。如下代码,已经封装了8大元素定位方法、截图方法、鼠标点击方法、警示框处理方法等,后续根据自己的需要自行补充丰富一些常用的功能函数或者方法。这里可以着重看下8大元素定位方法的封装~
'''Code description:页面基类,封装所有页面共用的方法Create time:2018-11-13Developer:
'''
# -*- coding: utf-8 -*-
import time
import os.path
from V2200.test.models.log import Logger
from selenium.common.exceptions import NoSuchElementException # selenium下封装的判断元素是否存在的模块
logger = Logger(logger='BasePage').getlog()
class BasePage(object):# 构造方法,初始化参数driver,用于后面的方法直接调用def __init__(self,driver):self.driver = driver# 浏览器前进def forward_browser(self):self.driver.forward()logger.info("在当前页面中点击浏览器前进.")# 浏览器后退def back_browser(self):self.driver.back()logger.info("在当前页面中点击浏览器后退.")# 设置隐式等待时间def wait(self,seconds):self.driver.implicitly_wait(seconds)logger.info("设置隐式时间:%d 秒." % seconds)# 关闭当前窗口def close_window(self):try:self.driver.close()logger.info("关闭当前窗口.")except NameError as e:logger.error("关闭当前窗口出错,抛出错误提示:%s." % e)# 截图功能:得到截图并保存图片到项目image目录下def get_window_img(self):file_path = os.path.dirname(os.path.abspath('.')) + '/image/' # 设置存放截图的路径# print('截图保存路径为:%s' % file_path)timeset = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time())) # 格式化时间pic_name = file_path + timeset + '.png' # 定义截图文件名称try:self.driver.get_screenshot_as_file(pic_name)logger.info('截图成功,图片保存路径为:/image.')except Exception as e :logger.error('截图出现异常',format(e))self.get_window_img()# 8大页面元素(对象)定位方法的封装def find_element(self,selector):'''使用‘=>’作为字符串分割符,后续实际测试用例根据输入的元素selector_by和selector_value 进行选择元素的定位类型:param selector::return: element'''element = ''if '=>' not in selector:return self.driver.find_element_by_id(selector)selector_by = selector.split('=>')[0] # 按=>分割符进行切割字符串,返回一个列表,得到列表的第一个元素,即元素的定位方法selector_value = selector.split('=>')[1] # 得到列表的第二个元素,即元素定位的值if selector_by == 'i' or selector_by == 'id':try:element = self.driver.find_element_by_id(selector_value)logger.info("定位元素OK,实际定位元素方法:%s ,定位的元素的属性值:%s" % (selector_by,selector_value))except NoSuchElementException as e:logger.error("没找到元素,抛出异常:%s" % e)self.get_window_img() # 截取当前窗口elif selector_by == 'n' or selector_by == 'name':element = self.driver.find_element_by_name(selector_value)elif selector_by == 'c' or selector_by == 'class_name':element = self.driver.find_element_by_class_name(selector_value)elif selector_by == 'l' or selector_by == 'link_text':element = self.driver.find_element_by_link_text(selector_value)elif selector_by == 'p' or selector_by == 'partial_link_text':element = self.driver.find_element_by_partial_link_text(selector_value)elif selector_by == 't' or selector_by == 'tag_name':element = self.driver.find_element_by_tag_name(selector_value)elif selector_by == 'x' or selector_by == 'xpath':try:element = self.driver.find_element_by_xpath(selector_value)logger.info("定位元素OK,实际定位元素方法:%s ,定位的元素的属性值:%s" % (selector_by, selector_value))except NoSuchElementException as e:logger.error("没找到元素,抛出异常:%s" % e)self.get_window_img() # 截取当前窗口elif selector_by == 'c' or selector_by == 'css_selector':element = self.driver.find_element_by_css_selector(selector_value)else:raise NameError("请输入正确的目标元素类型.")return element # 返回变量element# 封装输入框方法def type(self,selector,text):el = self.find_element(selector)el.clear()try:el.send_keys(text)logger.info("输入的文本内容为:%s" % text)except NameError as e:logger.error("输入的内容异常,抛出异常:%s" % e)self.get_window_img()# 清除文本内容def clear(self,selector):el = self.find_element(selector)try:el.clear()logger.info("清除输入框文本信息OK")except NameError as e:logger.error("清除输入框内容失败:抛出异常: %s" % e)self.get_window_img()# 封装点击元素的动作def click(self,selector):el = self.find_element(selector)try:el.click()logger.info("点击元素动作完成")except NameError as e:logger.error("点击事件失败,抛出异常:%s" % e)# 获取打开的url地址标题def get_page_title(self):logger.info("当前打开的url地址标题为:%s" % self.driver.title)return self.driver.title# 获取警示框,并得到提示框信息和关闭提示框def get_alert(self):el = self.driver.switch_to.alert # 获取窗口弹窗的方法try:assert '用户名或者密码错误' in el.text # el.text方法获取提示框内容logger.info("弹窗提示正确")el.accept() # 点击弹窗确认按钮except Exception as e:print('弹窗提示错误', format(e))@staticmethod # 静态方法:不强制要求传递参数,类可以不用实例化就能调用该方法def sleep(seconds):time.sleep(seconds)logger.info("等待时间是:%s 秒" % seconds)
七、登陆页面元素的封装
在上面我们实现了页面基类的封装,下图为楼主公司的一个软件登陆页面,在page_obj目录下新建home_page.py实现这个登陆页面元素定位和元素操作方法的封装
代码如下:
'''Code description: 继承基类,封装登陆页面所有的元素和元素的操作方法Create time:2018-11-16Developer:
'''
# -*- coding: utf-8 -*-
from V2200.test.models.base_page import BasePage
import xlrd # excel操作相关的模块
from V2200.test.models.log import Logger
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陆页面业务组件')
logger = Logger(logger='HomePage').getlog()
class HomePage(BasePage):def __init__(self,driver):BasePage.__init__(self,driver) # 继承父类,并调用父类的初始化方法self.input_username = table_sheetName.cell(1,1).value # 读取excel表中用户名输入框元素self.input_password = table_sheetName.cell(2,1).value # 读取excel表中密码输入元素self.rempwd = table_sheetName.cell(3,1).value # 读取excel表中是否记住密码按钮元素self.loginBtn = table_sheetName.cell(4,1).value # 读取excel表中登陆按钮元素self.centerBtn = table_sheetName.cell(6,1).value # 读取excel表中切换到中心用户的按钮logger.info("读取excel文件中登陆页面相关元素数据完成")def center_user(self):self.click(self.centerBtn)def user(self,text):self.type(self.input_username,text)def pwd(self,text):self.type(self.input_password,text)def ifrempwd(self):self.click(self.rempwd)def login(self):self.click(self.loginBtn)
这里引入了第三方的xlrd模块,用于读取excel文件中的数据,当然还有xlwt模块用于向excel写数据,说白了这两个模块就是实现操作excel,上面代码只用到了xlrd模块,在编写上面登陆页面的封装前,咱们先将登陆页面定位的元素和元素属性写到对应的excel表中,这样做的好处就是实现测试数据和测试脚本的分离,如果页面元素发生变化,那么我们就只需要修改excel中的元素属性而不需要修改代码,在data/testdata目录下新建名称为elementData.xlsx的文件,excel编辑内容如下:
后续各个页面的元素都是定位以及元素的数据都是可以写在不同的sheet中,至于xlrd模块具体向excel中读数据的方法以及使用,这里也是不做介绍,自己百度学习练习下就知道了。
软件其他页面的封装也是类似,按照上面的思想来就OK了。
八、登陆页面测试脚本的编写
通过上面封装的基类和登陆页面类,在unittest框架下开始编写具体的测试脚本。测试脚本在testcase下,如登陆功能的脚本我们写在:testcase/login_page_case目录下,其他页面的脚本写在对应的目录下。在testcase/login_page_case目录下创建:test_login_success.py和test_login_unsuccess.py分别表示登陆成功的脚本和登陆不成功的脚本。如下代码:
登陆成功代码:
'''Code description:测试登陆 Create time:2018-11-20 Developer:
'''
# -*- coding: utf-8 -*-
import unittest
# unittest执行测试用例,默认是根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。
import time
from V2200.test.models.browser_engine import BrowserEngine
from V2200.test.page_obj.home_page import HomePage
import xlrd
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陆页面业务组件')
class Login(unittest.TestCase):@classmethoddef setUpClass(cls):# 测试前置条件browser = BrowserEngine(cls)cls.driver = browser.open_browser(cls)@classmethoddef tearDownClass(cls):# 测试结束后环境的复原cls.driver.quit()
# case1:正确的用户密码登陆def test_1_login_sucess(self):homepage = HomePage(self.driver)homepage.user(table_sheetName.cell(1,2).value) # 读取excel中的数据homepage.pwd(table_sheetName.cell(2,2).value)homepage.ifrempwd()homepage.login()time.sleep(2)try:assert '视频监控' in homepage.get_page_title()print('test title success')homepage.get_window_img() # 调用Basepage类封装的截图方法except Exception as e:print('test title error', format(e))if __name__ == '__main__':unittest.main() # 将一个单元测试模块变成可以直接运行的测试脚本
登陆不成功代码:
'''Code description:测试登陆 Create time:2018-11-20 Developer:
'''
# -*- coding: utf-8 -*-
import unittest
# unittest执行测试用例,默认是根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。
import time
from V2200.test.models.browser_engine import BrowserEngine
from V2200.test.page_obj.home_page import HomePage
import xlrd
# from V2200.test.page_obj.link_page import LinkPage
excelfile_path = 'E:\V2200_AutoTest\V2200\data\\testdata\elementData.xlsx'
workbook = xlrd.open_workbook(excelfile_path)
table_sheetName = workbook.sheet_by_name('登陆页面业务组件')
class LoginUnsuccess(unittest.TestCase):@classmethoddef setUpClass(cls):# 测试前置条件browser = BrowserEngine(cls)cls.driver = browser.open_browser(cls)@classmethoddef tearDownClass(cls):# 测试结束后环境的复原cls.driver.quit()
# case2:错误的用户+正确的密码登陆def test_2_login_erroruser(self):homepage = HomePage(self.driver)homepage.user(table_sheetName.cell(1, 3).value) # 读取excel中的数据homepage.pwd(table_sheetName.cell(2, 3).value)homepage.ifrempwd()homepage.login()time.sleep(2)try:assert '视频监控' in homepage.get_page_title()print('test title success')homepage.get_window_img() # 调用Basepage类封装的截图方法except Exception as e:print('test title error',format(e))# case3:正确的用户+错误的密码登陆def test_3_login_errorpasswd(self):homepage = HomePage(self.driver)homepage.user(table_sheetName.cell(1, 4).value) # 读取excel中的数据homepage.pwd(table_sheetName.cell(2, 4).value)homepage.ifrempwd()homepage.login()time.sleep(2)try:assert '视频监控' in homepage.get_page_title()print('test title success')homepage.get_window_img() # 调用Basepage类封装的截图方法except Exception as e:print('test title error', format(e))
# case4:错误的用户+错误的密码登陆def test_4_login_erroruser_errorpasswd(self):homepage = HomePage(self.driver)homepage.user(table_sheetName.cell(1, 5).value) # 读取excel中的数据homepage.pwd(table_sheetName.cell(2, 5).value)homepage.ifrempwd()homepage.login()time.sleep(2)try:assert '视频监控' in homepage.get_page_title()print('test title success')homepage.get_window_img() # 调用Basepage类封装的截图方法except Exception as e:print('test title error', format(e))if __name__ == '__main__':unittest.main() # 将一个单元测试模块变成可以直接运行的测试脚本
九、测试执行控制模块
完成上面的测试脚本编写后,对于自动化测试还需要有一个测试执行控制的部分,用来控制执行哪些用例集,生成HTML可视化的测试报告,并实现测试报告邮件发送。
在runtest目录下新建run_all_case.py,编辑如下代码:
'''Code description: TestLoader测试case,并执行得到的所有测试集,生成html文件的测试报告并邮件发送测试报告Create time:2018-11-20Developer:
'''
# -*- coding: utf-8 -*-
import HTMLTestRunner1 # 导入开源的测试报告生成HTML格式的模块
import os.path
import time
import unittest
import configparser # 解析配置文件模块
from email.mime.text import MIMEText
from email.header import Header
import smtplib
"""
发邮件需要用到python两个模块,smtplib和email,这俩模块是python自带的,只需import即可使用。
smtplib模块主要负责发送邮件,email模块主要负责构造邮件。
其中MIMEText()定义邮件正文,Header()定义邮件标题。MIMEMulipart模块构造带附件"""
# ===============定义邮件发送============
def send_mail(file_new):config = configparser.ConfigParser()file_path = os.path.dirname(os.path.abspath('.')) + '/V2200/config/config.ini'config.read(file_path, encoding='UTF-8') # 读取config配置文件emailserver = config.get("emailserver", "emailservice")from_user = config.get("emailfrom_user", "from_user")from_passwd = config.get("emailfrom_passwd", "from_passwd")to_user = config.get("emailto", "to_user")f = open(file_new,'rb')mail_boy = f.read()f.close()msg = MIMEText(mail_boy,'html','utf-8') # 定义邮件正文msg['Subject'] = Header('V2200自动化测试报告','utf-8') # 定义邮件标题smtp = smtplib.SMTP()smtp.connect(emailserver) # 连接邮箱服务器smtp.login(from_user,from_passwd) # 邮件发送方登陆smtp.sendmail(from_user,to_user,msg.as_string()) # 邮件发送者和接收者smtp.quit()print("邮件已经发送,请注意查收!")# ==============找到最新生成的测试报告文件===========
def new_report(report_path):lists = os.listdir(report_path) # 得到项目目录下所有的文件和文件夹lists.sort(key=lambda fn:os.path.getmtime(report_path + '\\' + fn)) # 将得到的文件和文件夹按创建时间排序file_new = os.path.join(report_path,lists[-1]) # 获取最新创建的文件print(file_new)return file_new
# 测试用例路径
# case_path = os.path.join(os.getcwd(),'testcase')
case_path = os.path.abspath('E:\V2200_AutoTest\\testcase')
print(case_path)
# 测试报告路径
report_path = os.path.abspath('E:\V2200_AutoTest\\testreport')
print(report_path)
def all_case():'''找到case_path路径下所有以test_login开头的测试用例文件,保证每个子目录都是一个包文件,即该目录下有__init__.py文件,才能获取到多个目录下的所有test*.py的文件下的所有测试用例'''all_case = unittest.defaultTestLoader.discover(case_path,pattern="test_login*.py",top_level_dir=None)print(all_case)return all_case
if __name__ == '__main__':# 获取当前时间,并格式化时间now_time = time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))# html测试报告路径report_html = os.path.join(report_path,"result_"+now_time+".html")fp = open(report_html,'wb') # 打开一个文件,将测试结果写入该文件中'''wb:以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件'''runner = HTMLTestRunner1.HTMLTestRunner(stream=fp,title=u'V2200自动化测试报告,测试结果如下:',description=u'用例执行情况:')runner.run(all_case()) # 执行所有测试casefp.close()mail_report = new_report(report_path)send_mail(mail_report)
该代码,编写了怎么获取需要执行的测试用例脚本和引入生成可视化测试报告的模块和发送邮件模块等,对于这几个模块自己多学习下就能掌握。
十、测试执行效果
通过上面这些类的封装以及测试脚本的编写,算是完成了我们自动化测试框架的基本具备的东西。忙活了这么久,是时候来看看咱们的效果了。PyCharm中运行run_all_case.py,运行完成后的效果如下:
咱们再看看log/logs路径下生成的日志,就如下图这样:
同时执行完成后,在testreport目录下会生成HTML格式的可视化测试报告文件,用浏览器打开效果如下:
这报告是不是很酷炫啊,O(∩_∩)O哈哈~
还有测试报告发送邮件给到指定的邮箱哦,如果你的自动化测试执行完了,可以把该自动化测试报告自动邮件发给你的leader,领导看到了是不是对你另眼相看?楼主上面的代码设置发送的是楼主公司内网使用的邮箱:foxmail,效果如下:
到这里算是完成了咱们自动化测试框架,并取得了一定的成果~~
十一、整个自动化测试框架的总结和反思
其实到第十节的介绍,楼主算是成功的做出了一个轻量级的测试框架,but,回过头来继续思考,还是有诸多需要优化和待下一步解决的问题:
1.页面基类还需要补充更多的公共函数或者方法;
2.可视化HTML测试报告内容还不够丰富,没有完善的测试执行失败的用例的详细描述和测试截图附件显示;
3.整个框架的部分逻辑还需要优化和改进;
4.待解决的问题:没实现测试脚本的持续集成和定时执行,现在想到的是配合jenkins持续集成来达到自动构建测试执行任务;
5.想独立开发一个web测试平台,现在想到的是学习Django的web框架来开发一个自动化测试平台;
对于这样不足和构想,楼主也是会继续学习相关的知识,并一步步实现它,对于看到该博客的朋友们也可以给楼主一些好的建议和指出错误,希望有对自动化测试有兴趣的朋友,大家共同学习和进步哦。
感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。 如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出。
写在最后
这篇贴子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。
都到这了记得三连支持一下吧。
完整版文档下载方式:
这些资料,对于从事【软件测试】等相关工作的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享。
在评论区和我互动交流或者私❤我【软件测试学习】领取即可,拿走不谢。
相关文章:
Python3+Selenium2完整的自动化测试实现之旅(七):完整的轻量级自动化框架实现
一、前言 前面系列Python3Selenium2自动化系列博文,陆陆续续总结了自动化环境最基础环境的搭建、IE和Chrome浏览器驱动配置、selenium下的webdriver模块提供的元素定位和操作鼠标、键盘、警示框、浏览器cookie、多窗口切换等场景的方法、web自动化测试框架、python面…...
泰山信息科技5周年:无尽的感恩,非常非常的惋惜
去年的时候,庆贺4周年,公司员工一起去某个地方玩(确实没吃到什么东西)。这是当时的情形: 因为各种原因,今年3月无锡研发基地解散。作为技术总监,我是非常非常的惋惜。因为我真的想把泰山OFFICE做…...
LabVIEW编程开发PCB测试仪
LabVIEW编程开发PCB测试仪 使用PXI和LabVIEW的PCB钉床测试仪 用于PCB(印刷电路板)的钉床测试仪,使用PXI和LabVIEW。一家电子制造公司需要测试仪来测试他们的PCB产品。钉床测试仪是一种具有连接到电路板上各个测试点的引脚的测试。电路板需要…...
React使用Electron开发桌面端
React是一个流行的JavaScript库,用于构建Web应用程序。结合Electron框架,可以轻松地将React应用程序打包为桌面应用程序。以下是使用React和Electron开发桌面应用程序的步骤: 1. 安装Electron 首先,你需要安装Electron。在终端中…...
springboot+vue餐厅点餐系统在线点餐系统(含源码+数据库)
1.系统分析 系统用例图如下所示。 从用户、餐厅等方面进行需求分析如下。 1.用户需求:系统应该提供简单易用的用户界面,用户可以浏览餐厅菜单,选择菜品,下订单。此外,应该允许用户管理个人信息和查看历史订单。 2.餐…...
Vue.js 中的 TypeScript 支持是什么?如何使用 TypeScript?
Vue.js 中的 TypeScript 支持 Vue.js 是一款流行的前端框架,它提供了一种简单、灵活的方式来构建用户界面。随着 TypeScript 的普及,Vue.js 也开始支持 TypeScript,使得开发者可以使用类型检查等 TypeScript 特性来提高代码质量和可维护性。…...
测试者必知—如何做Web测试?常见测试点总结
目录 前言: 一、Web应用程序 二、功能测试 三、易用性测试(界面测试) 四、兼容性测试 五、安全性测试 六、性能测试 前言: Web测试是指对基于Web技术的应用程序进行测试,以测试其功能、性能、安全和稳定性等方面的表…...
怎么转换英文音频成文字?英文音频转文字app分享
两位朋友正在讨论如何将一段英文讲座的音频转换成文字,以便于学习和理解。 Sophia:嗨,我最近听了一段非常精彩的英文讲座,但是对于我来说,理解听到的内容有些困难。你知道有什么方法可以将英文音频转换成文字吗&#…...
esp32-cam拍照上传,app inventor 制作安卓app实时显示
1、ESP32-cam开发环境配置 本例程 是利用arduino IDE开发,关于arduino IDE 的esp32环境配置可参考:环境配置: 点击跳转 安装好esp32 环境,开发板选择esp32 wrover module开发板,其他默认即可。 2 、程序下载 示例程序下载:点击下载 需要修改的信息有WIF名称,WIFI密码,…...
基于jsp+mysql+Spring+mybatis+Springboot的Springboot实现的就业信息管理平台
运行环境: 最好是java jdk 1.8,我在这个平台上运行的。其他版本理论上也可以。 IDE环境: Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以,如果编译器的版本太低,需要升级下编译器,不要弄太低的版本 tomcat服务器环…...
阿里巴巴内部10w字Java面试小抄火了,完整版开放下载
Java 面试 “金九银十”这个字眼对于程序员应该是再熟悉不过的了,每年的金九银十都会有很多程序员找工作、跳槽等一系列的安排。说实话,面试中 7 分靠能力,3 分靠技能;在刚开始的时候介绍项目都是技能中的重中之重,它…...
Logback自定义DBAppender保存系统日志到数据库
在系统中采用了spring boot logback+slf4j的日志框架,将系统日志记录到数据库。 相关参考来源: 官方文档-DBAppender Logback输出日志到自定义MySQL数据库(重写DBAppender) logback日志框架中filter的使用 1. 添加依…...
云原生之使用Docker部署LimeSurvey在线调查工具
云原生之使用Docker部署LimeSurvey在线调查工具 一、LimeSurvey介绍1.1 LimeSurvey简介1.2 LimeSurvey特点1.3 LimeSurvey使用场景1.4 LimeSurvey支持版本二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.检查doc…...
sdbusplus:添加ObjectManager interface
ObjectManager接口可以一次性拿到对象及子对象的所有property,在交互中经常会用到。 sdbusplus提供了add_manager完成该接口的添加: //server_obj.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server.hpp> #include <sd…...
“RAID0 vs RAID1 vs RAID5 vs RAID6 vs RAID10:哪种RAID级别最适合你的需求?“
概要: RAID(Redundant Array of Independent Disks)是一种数据存储技术,可以将多个硬盘组合起来以提高性能、可靠性和容错能力。下面是几种常见的RAID级别,以及它们的用途和特点。 目录 RAID 0RAID 1RAID 5RAID 6RAID…...
【MySQL】Mycat
文章目录 什么是Mycat为什么要用Mycatmycat能干什么各数据库中间件对比Mycat原理数据库中间件逻辑库逻辑表分片表分片规则全局表ER表非分片表分片节点节点主机mycat安装mycat核心配置schema.xmlserver.xmlrule.xml加密明文密码(可选) MyCat读写分离垂直拆…...
Netty中ServerBootstrap类介绍
一、Netty基本介绍 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。 Netty 是一…...
数字图像处理实验报告
目录 实验二、图像在空间域上的处理方法 实验三、图像在频率域上的处理方法 实验二、图像在空间域上的处理方法 一、实验目的 了解图像亮(灰)度变换与空间滤波的意义和手段;熟悉图像亮(灰)度变换与空间滤波的MATLA…...
【C51】10-基础51单片机的小车项目(51完结)
10.1小车的安装 10.2电机模块的开发(L9110S) 接通 VCC , GND 模块电源指示灯亮, 以下资料来源官方,但是不对,根据下节课实际调试 IA1 输入高电平, IA1 输入低电平,【 OA1 OB1 】电…...
进程、线程、锁阶段总结汇总
目录 进程 线程 锁 由于进程线程和锁的方面比较陌生,并且繁杂,所以简单总结一下学习到的函数API 进程 子进程创建 fork(); 进程结束 exit(); 进程回收 wait(); 进程回收 waitpad(); //函数可以指定进程组中的任意子进程,可以设置特殊…...
Filters.jar图片转素描
链接:https://pan.baidu.com/s/1ATlC2l1I83TPYFomHiWuFg?pwd2vm5 提取码:2vm5...
将MSYS2 MinGW集成到Windows终端
微软开发了一款Windows终端的开源软件,非常好用。安装后在Win7及以上系统会在右键菜单中添加一条“在终端中打开”的命令,非常方便。它默认配置了Windows命令行以及PowerShell,如果安装了Visual Studio 2022还会配置Visual Studio 2022的命令…...
SpringBoot项目使用slf4j的MDC日志打点功能
SpringBoot项目使用slf4j的MDC日志打点功能 物料准备: 1.自定义1个线程MDC打点工具类 2.配置logback打印MDC打点的traceId 3.配置webMVC使用MDC打点 4.配置ThreadPoolTaskExecutor使用MDC打点 5.配置HttpClient使用MDC打点 6.测试MDC日志打点效果 线程mdc打…...
宝塔修改默认端口后面板打不开
1、查看防火墙开启的端口,发现没有开启8888 [rootVM-12-12-centos ~]# firewall-cmd --list-ports 20/tcp 21/tcp 22/tcp 80/tcp 888/tcp 8081/tcp 39000-40000/tcp 8081/udp 2、防火墙开启8888端口 [rootVM-12-12-centos ~]# firewall-cmd --zonepublic --add-por…...
tinkerCAD案例:3.基本按钮
基本按钮 在本课中,您将学习制作具有圆柱形状的基本按钮。 说明 将圆柱体拖动到工作平面。 将其缩小到 2 毫米的高度。 提示: 您可以使用圆柱形状顶部的白点缩小圆柱体。 将其缩小到直径 16 毫米。 这将是按钮的主要形状。 现在我们可以创建允许将纽…...
客户线上反馈:从信息搜集到疑难 bug 排查全流程经验分享
写在前面:本文是我在前端团队的第三次分享,应该很少会有开发者写客户反馈处理流程以及 bug 排查的心得技巧,全文比较长,写了一个多星期大概1W多字(也是我曾经2年工作的总结),如果你有耐心阅读&a…...
悲观锁、乐观锁、自旋锁
悲观锁、乐观锁、自旋锁 (1)乐观锁 乐观锁是一种乐观的思想,即认为读多写少,遇到并发的可能性低,每次拿数据时都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有…...
七、进程地址空间
一、环境变量 (一)概念 环境变量(environment variables):系统当中用做特殊用途的系统变量。 如:我们在编写C/C代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可…...
浅谈智能微电网供电系统的谐波治理
摘要:智能微电网供电系统的特性容易引发谐波,而谐波导致电力损耗加大,降低供电质量。本文从谐波的产 生原因和危害做出详细阐述,并结合智能微电网提出了治 理谐波的方法和措施。 关键词:智能微电网;谐波危害…...
springboot项目的社区/博客系统
课前导读: 你学完一篇,你就多会一项技能,多多少少对你还是有点帮助的不是吗?~~~ 这是博主网页的url:优文共享社区 开发环境:JDK1.8,IDEA2021,MySQL5.7,Windows11 开发技术…...
手机端的网站首页该怎么做/电脑培训学校哪家好
由于工作需要,需要搭建hadoopzookeeperhbasestormkafka集群准备了三台服务器(一台8核32G内存300G硬盘充当master,一台8核16G内存300G硬盘充当slave01,一台816G500G硬盘充当slave02,并且都能上网)࿰…...
建设部中国建造师网查询/网站怎么优化关键词排名
查看垃圾回收情况并对内存进行转储查询JAVA线程#ps -ef | grep java或#jps2.查看垃圾回收总体统计情况# jstat -gcutil pidS0:幸存1区当前使用比例S1:幸存2区当前使用比例E:伊甸园区使用比例O:老年代使用比例M:元数据区…...
太原seo网站建设/推广公司哪家好
win10安装软件出现error launching installer提示怎么办?我们在平时的工作当中,经常会安装很多软件,但有时会遇到安装失败,提示“error launching installer”的问题,遇到此问题的用户,请来看看下面的解决吧。最近有位…...
做网站要注意什么问题/百度贴吧官网网页
JVM类加载过程JVM类加载过程分为几个阶段,分别是加载、验证、准备、解析和初始化。加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初始值,解析是把常量池内的…...
网站设计开发软件有哪些/怎么注册域名
QT怎么自动一次性修改、替换程序中所有变量名? 熟悉的编译器都有这个功能, 初学QT,搜索无果,找了一会儿,分析给大家。 最直接的办法:选中变量名,CtrlShiftr ,变成红色之后即可更改。. 选中变…...
什么网站可以做微官网/谷歌搜索引擎官网
一、为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1、为对应的资源分配内存 2、初始化内存 3、使用资源 4、清理资源 5、释放内存 应用程序对资源(内存使用)管理的方式,常见的一般有如下几种࿱…...