Python3+Selenium2完整的自动化测试实现之旅(七):完整的轻量级自动化框架实现

news2024/12/30 3:24:45

一、前言

        前面系列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-12
  Developer:
'''
# -*- 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()    # 实例化对象logger
 
class 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)                        # 得到测试的url
        logger.info("浏览器的版本为:%s" % driver.capabilities['version'])  # 获取浏览器版本
        driver.maximize_window()
        logger.info("最大化浏览器窗口.")
        driver.implicitly_wait(10)
        return driver
 
    def quit_browser(self):
        self.driver.quit()

     这样我们就自定义封装了一个简单的日志类模块,设置了日志输出级别、输出格式以及输出日志的位置,后面其他模块需要输出日志时,就可以调用引入该日志类。

 五、浏览器模块的实现

     日志类实现简单封装后,继续造轮子~。此部分用于封装浏览器模块,主要实现打开和关闭不同浏览器的方法,这里就用到了POM的思想,咱们封装了浏览器的类型和打开关闭方法,那么后面每条测试脚本就可以直接调用打开和关闭浏览器方法,脚本只需要专注具体的测试业务逻辑的实现即可。在models目录下新建broser_engine.py文件,自定义一个叫做BrowserEngine类,实现浏览器模块的封装,代码如下:

'''
  Code description:封装浏览器引擎类,读取配置文件实现浏览器类型的选择,并封装打开浏览器和退出的方法
  Create time:2018-11-12
  Developer:
'''
# -*- 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()    # 实例化对象logger
 
class 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)                        # 得到测试的url
        logger.info("浏览器的版本为:%s" % driver.capabilities['version'])  # 获取浏览器版本
        driver.maximize_window()
        logger.info("最大化浏览器窗口.")
        driver.implicitly_wait(10)
        return driver
 
    def quit_browser(self):
        self.driver.quit()

 六、页面基类的实现

        此部分用于封装页面基类,主要用于封装一些常用的公共方法,如截图方法、元素定位方法、元素通用操作方法、警示框处理方法等等,只要软件页面一些常用的操作都可以写在该页面基类中,这个页面基础类就类似于一个公共函数库一样,封装这些方法,后面有需要的地方直接调用即可。如下代码,已经封装了8大元素定位方法、截图方法、鼠标点击方法、警示框处理方法等,后续根据自己的需要自行补充丰富一些常用的功能函数或者方法。这里可以着重看下8大元素定位方法的封装~

'''
  Code description:页面基类,封装所有页面共用的方法
  Create time:2018-11-13
  Developer:
'''
# -*- 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-16
  Developer:
'''
# -*- 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):
    @classmethod
    def setUpClass(cls):
        # 测试前置条件
        browser = BrowserEngine(cls)
        cls.driver = browser.open_browser(cls)
    @classmethod
    def 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):
    @classmethod
    def setUpClass(cls):
        # 测试前置条件
        browser = BrowserEngine(cls)
        cls.driver = browser.open_browser(cls)
    @classmethod
    def 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-20
  Developer:
'''
# -*- 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())   # 执行所有测试case
    fp.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框架来开发一个自动化测试平台;

    对于这样不足和构想,楼主也是会继续学习相关的知识,并一步步实现它,对于看到该博客的朋友们也可以给楼主一些好的建议和指出错误,希望有对自动化测试有兴趣的朋友,大家共同学习和进步哦。


感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。 如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出。

写在最后

这篇贴子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。

都到这了记得三连支持一下吧。

 完整版文档下载方式:

这些资料,对于从事【软件测试】等相关工作的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享。

在评论区和我互动交流或者私❤我【软件测试学习】领取即可,拿走不谢。

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

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

相关文章

如何系统的学习robo-gym

提醒:以下内容仅做参考,可自行发散。在发布作品前,请把不需要的内容删掉。无论是初学者还是有经验的专业人士,在学习一门新的IT技术时,都需要采取一种系统性的学习方法。那么作为一名技术er,你是如何系统的…

OpenPCDet安装、使用方式及自定义数据集训练

OpenPCDet安装、使用方式及自定义数据集训练 个人博客 OpenPCDet安装 # 先根据自己的cuda版本,安装对应的spconv pip install spconv-cu113# 下载OpenPCDet并安装 git clone https://github.com/open-mmlab/OpenPCDet.git cd OpenPCDet pip install -r requireme…

Jetpack Compose 中安全地消耗Flow

Jetpack Compose 中安全地消耗Flow 以 Lifecycle 为周期的方式收集流是在 Android 上收集流的推荐方式。如果您正在使用 Jetpack Compose 构建 Android 应用程序,则使用 collectAsStateWithLifecycle API 可以在 Lifecycle 为周期的方式下从您的 UI 中收集流。 co…

利用Jmeter做接口测试(功能测试)全流程分析

利用Jmeter做接口测试怎么做呢?过程真的是超级简单。 明白了原理以后,把零碎的知识点填充进去就可以了。所以在学习的过程中,不管学什么,我一直都强调的是要循序渐进,和明白原理和逻辑。这篇文章就来介绍一下如何利用…

CP2102 USB转UART国产桥接芯片 DPU02

芯片概述: DPU02是一个高度集成的USB转UART的桥接控制器,该产品提供了一个简单的解决方案,可将RS-232设计更新为USB设计,并简化PCB组件空间。该DPU02包括了一个USB2.0全速功能控制器、USB收发器、振荡器、EEPROM和带有完整调制解调控制信号的…

通过宝塔辅助部署本地Python爬虫项目到阿里云轻量服务器

文章目录 一、 上传项目文件二、准备项目环境2.1、安装 requirements.txt 依赖2.2、安装 node.js 环境2.3、阿里云服务器MySQL 8.0开启远程连接2.4、本地远程连接MySQL测试2.4.1、navicat 远程连接测试2.4.2、python 代码连接测试 笔记:最近想把本地的一套爬虫项目给…

SpringBoot——启动源码(一)

SpringBootApplication注解 前言SpringBootApplicationSpringBootConfigurationEnableAutoConfigurationimport注解 ComponentScan 总结 前言 Springboot作为Spring的脚手架,其本质核心并不提供Spring核心功能,作用就是开发者快速构建,预置三…

怎么转换英文音频成文字?英文音频转文字app分享

两位朋友正在讨论如何将一段英文讲座的音频转换成文字,以便于学习和理解。 Sophia:嗨,我最近听了一段非常精彩的英文讲座,但是对于我来说,理解听到的内容有些困难。你知道有什么方法可以将英文音频转换成文字吗&#…

SpringBoot内置Logback日志的学习

一、日志级别 日志级别有TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF。 TRACE级别最低 DEBUG 的级别低,当需要打印调试信息的话,就用这个级别,不建议在生产环境下使用。 INFO 的级别高一些,当一些重要的信息需要打印的时候&#x…

虹科多功能电流电压采集方案

01电流电压采集基础概念和应用 电流采集、电压采集、电能充电量测试和功率测试在不同领域都有着广泛的应用 ▲汽车电子:电池管理系统BMS、发动机控制系统、车身电子系统 ▲航空航天:飞行控制系统,航空电源管理系统、航空电子设备 ▲消费电…

如何学习和提升使用编程语言的能力? - 易智编译EaseEditing

学习编程语言并提升编程能力需要一定的学习方法和实践。以下是一些方法可以帮助你提升编程语言能力: 学习基本语法: 了解编程语言的基本语法和关键概念。可以通过阅读官方文档、教程、书籍或在线资源来学习。 编写代码: 编写实际的代码是提…

入门孪生网络Siamese Network,我将会分几个博客来逐步阐述我对孪生网络的理解和应用---初步介绍0

文章目录 前言一、孪生网络(Siamese Network)的基本概念二、孪生网络(Siamese Network)的优点三、利用孪生网络进行故障诊断/分类的思路假如我有一堆数据,它可以是轴承故障数值数据,也可以是图像数据,我想进行二分类&a…

ESP32-C2-12模组 AT固件例程

ESP32C2 AT固件使用 ESP32 C2模组,如图1-1所示 图1-1 ESP32 C2模组 ESP32 C2开发板,如图1-2所示 图1-2 ESP32 C2开发 方案亮点 1、完整的 WiFi 子系统,符合 IEEE 802.11b/g/n 协议,具有 Station 模式、SoftAP 模式、SoftAP Stat…

Vue中如何只传递一个人员Name同时把人员Id也传递过去

前言: 根据项目需求,在修改功能中,要求展示一个人员的下拉框,但是又要把人员ID在点击提交时传递过去,一般这种情况有2种解决方法:一是 通过map遍历匹配;二是 在选中人员时将人员ID获取到&#x…

编辑和校对魔法:让文字焕发生机的秘诀

编辑和校对是写作过程中的关键环节,可以让你的文字更加精炼、清晰、引人入胜。以下是一些编辑和校对的秘诀,可以让你的文字焕发生机。 1.保持客观 从读者的角度审视文章,保持客观和中立。确保内容清晰、观点明确,同时避免主观情感…

运筹系列81:LKH代码分析

1. 基本数据结构 基础的node定义在LKH.h中 用于2-level tree的segment定义如下: LKH可以使用3种数据结构,默认是2-level tree: 2-level tree的flip操作(即2-opt算子),在Flip_SL.c中,特殊的地…

请问python如何处理url带有“?”参数的接口?

在Python中处理带有"?"参数的URL接口,可以使用urllib.parse库中的urlencode()函数来进行编码。以下是一些示例代码 如果你想学习自动化测试,我这边给你推荐一套视频,这个视频可以说是B站百万播放全网第一的自动化测试教程&#x…

详解python排序的5种高级用法

来源:投稿 作者:Fairy 编辑:学姐 排序是编程中常用的操作之一。Python提供了多种排序方法,可以适用于不同的排序需求。 那么,今天我们将介绍Python中常用的 5 种列表排序方法。 「1.使用sorted()函数和lambda表达式」…

常见的JS存储方式及其特点

在前端开发中,经常需要在浏览器端存储和管理数据。为了实现数据的持久化存储和方便的访问,JavaScript提供了多种数据存储方式。本文将介绍几种常见的前端JS数据存储方式及其特点。 1. Cookie Cookie是一种小型的文本文件,由浏览器保存在用户…

VMware Workstation下载与安装(适用于在官网注册好账号的朋友,许可证秘钥请自行网上搜索获取)

一、VMware Workstation下载 第一步:点击下方“Resources”链接,进入Vmware官网(本教程适用于已经在官网注册好账号并已经进行登陆的朋友): Resources 第二步:点击“Product Downloads”,进入…