前言

自己从未没想过能使用python来做自动化测试框架的设计、开发。

可能有人会好奇说,六哥,你怎么也用python写测试框架了?

领导说:

python你也没有实际工作经验,可能就是自己自学的。

听完,那一刻,我真的特别证明自己,我也行!

框架搭建

整个框架的实现,大约也就1.5天,关于框架的开发并不是很难,主要难在测试报告增加失败自动截图功能echart的饼子图统计功能,两者的整合花了近半天的时间吧。

效果:

1、核心思想

延续使用Page ObjectPage Factory思想,使页面、数据、元素、脚本进行分离,此处演示仅仅为了讲解框架搭建思路,并非为我在公司写的那套框架,主要使用selenium4+python3+pytest,这里只贴核心代码,仅供学习交流使用。

目录结构

2、日志封装

主要用于方便定位用例脚本执行步骤,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/7 19:36
@Auth : 软件测试君
@File :LogUtils.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
import time
import os
import logging currrent_path = os.path.dirname(__file__)
log_path = os.path.join(currrent_path, '../logs') class LogUtils: def __init__(self, log_path=log_path):
"""
通过python自带的logging模块进行封装
"""
self.logfile_path = log_path
# 创建日志对象logger
self.logger = logging.getLogger(__name__)
# 设置日志级别
self.logger.setLevel(level=logging.INFO)
# 设置日志的格式
formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
"""在log文件中输出日志"""
# 日志文件名称显示一天的日志
self.log_name_path = os.path.join(self.logfile_path, "log_%s" % time.strftime('%Y_%m_%d')+".log")
# 创建文件处理程序并实现追加
self.file_log = logging.FileHandler(self.log_name_path, 'a', encoding='utf-8')
# 设置日志文件里的格式
self.file_log.setFormatter(formatter)
# 设置日志文件里的级别
self.file_log.setLevel(logging.INFO)
# 把日志信息输出到文件中
self.logger.addHandler(self.file_log)
# 关闭文件
self.file_log.close() """在控制台输出日志"""
# 日志在控制台
self.console = logging.StreamHandler()
# 设置日志级别
self.console.setLevel(logging.INFO)
# 设置日志格式
self.console.setFormatter(formatter)
# 把日志信息输出到控制台
self.logger.addHandler(self.console)
# 关闭控制台日志
self.console.close() def get_log(self):
return self.logger logger = LogUtils().get_log() if __name__ == '__main__':
logger.info('123')
logger.error('error')

3、基础页面

用于存放,控件及API的常用操作,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/7 19:58
@Auth : 软件测试君
@File :BasePage.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
import time from selenium.common import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait as WD from util.LogUtils import LogUtils
from util.ParseConFile import ParseConFile logger = LogUtils().get_log() class BasePage(object):
"""控件及API的常用操作""" cf = ParseConFile() def __init__(self, driver, timeout=30):
self.byDic = {
'id': By.ID,
'name': By.NAME,
'class_name': By.CLASS_NAME,
'xpath': By.XPATH,
'link_text': By.LINK_TEXT,
'css': By.CSS_SELECTOR
}
self.driver = driver
self.outTime = timeout def find_element(self, by, locator):
"""
通过id, name, xpath, css,class....,查找元素
"""
try:
logger.info("通过 " + by + " 定位")
element = WD(self.driver, self.outTime).until(lambda x: x.find_element(self.byDic.get(by), locator))
except TimeoutException as e:
logger.error('请确认元素定位方式,' + e)
else:
return element def find_elements(self, by, locator):
"""
通过id, name, xpath, css,class....,查找一组元素
"""
try:
logger.info("通过 " + by + " 定位")
elements = WD(self.driver, self.outTime).until(lambda x: x.find_elements(self.byDic.get(by), locator))
except TimeoutException as e:
logger.error('请确认元素定位方式,' + e)
else:
return elements def get_text(self, by, locator):
"""
获取元素文本/属性信息
"""
logger.info("获取元素文本成功!")
return self.find_element(by, locator).text def open_url(self, url):
"""打开浏览器"""
logger.info("打开项目首页:" + url)
self.driver.get(url) def quit_browser(self):
self.driver.quit() def send_keys(self, by, locator, keys=''):
"""输入操作"""
logger.info("输入:" + keys)
self.find_element(by, locator).clear
self.sleep(1)
self.find_element(by, locator).send_keys(keys) def click(self, by, locator):
"""点击操作"""
logger.info("点击按钮:" + locator)
self.find_element(by, locator).click() @staticmethod
def sleep(num=0):
"""强制等待"""
logger.info("程序等待:" + str(num) + " 秒")
time.sleep(num)

4、登陆页面

主要用于存放控件及元素操作,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/7 20:27
@Auth : 软件测试君
@File :LoginPage.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
from Page.BasePage import BasePage
from util.LogUtils import LogUtils
from util.ParseConFile import ParseConFile logger = LogUtils().get_log() class LoginPage(BasePage):
"""
存放控件及元素操作
"""
# 配置文件读取元素
do_conf = ParseConFile()
# 用户名输入框
username = do_conf.get_locator('LoginPage_Elements', 'username')
# 密码输入框
password = do_conf.get_locator('LoginPage_Elements', 'password')
# 登录按钮
loginBtn = do_conf.get_locator('LoginPage_Elements', 'loginBtn')
# 登录失败的提示信息
error_msg = do_conf.get_locator('LoginPage_Elements', 'errorMsg') def login(self, username, password):
"""登录流程"""
self.open()
self.send_username(username)
self.send_password(password)
self.click_login_btn()
msg = self.get_errorMsg()
return msg def open(self):
self.open_url('http://localhost:8080/login') def quit(self):
self.quit_browser() def send_username(self, username):
self.send_keys(*LoginPage.username, username) def send_password(self, password):
self.send_keys(*LoginPage.password, password) def click_login_btn(self):
self.click(*LoginPage.loginBtn) def get_errorMsg(self):
return self.get_text(*LoginPage.error_msg) if __name__ == "__main__":
pass

5、业务操作

主要用于记录用例步骤,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/7 20:27
@Auth : 软件测试君
@File :LoginPage.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
from Page.BasePage import BasePage
from util.LogUtils import LogUtils
from util.ParseConFile import ParseConFile logger = LogUtils().get_log() class LoginPage(BasePage):
"""
存放控件及元素操作
"""
# 配置文件读取元素
do_conf = ParseConFile()
# 用户名输入框
username = do_conf.get_locator('LoginPage_Elements', 'username')
# 密码输入框
password = do_conf.get_locator('LoginPage_Elements', 'password')
# 登录按钮
loginBtn = do_conf.get_locator('LoginPage_Elements', 'loginBtn')
# 登录失败的提示信息
error_msg = do_conf.get_locator('LoginPage_Elements', 'errorMsg') def login(self, username, password):
"""登录流程"""
self.open()
self.send_username(username)
self.send_password(password)
self.click_login_btn()
msg = self.get_errorMsg()
return msg def open(self):
self.open_url('http://localhost:8080/login') def quit(self):
self.quit_browser() def send_username(self, username):
self.send_keys(*LoginPage.username, username) def send_password(self, password):
self.send_keys(*LoginPage.password, password) def click_login_btn(self):
self.click(*LoginPage.loginBtn) def get_errorMsg(self):
return self.get_text(*LoginPage.error_msg) if __name__ == "__main__":
pass

6、测试报告之失败带截图

这块确实很坑,看了很多网上的教程,笔者不才,整了一下午才弄出失败带截图,主要是对conftest.py的设计编写,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/10 18:13
@Auth : 软件测试君
@File :conftest.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
import pytest
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager driver = None @pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', []) if report.when == 'call' or report.when == "setup":
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
file_name = report.nodeid.replace("::", "_") + ".png"
screen_img = _capture_screenshot()
if file_name:
html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:600px;height:300px;" ' \
'οnclick="window.open(this.src)" align="right"/></div>' % screen_img
extra.append(pytest_html.extras.html(html))
report.extra = extra @pytest.fixture(scope='session')
def browser():
global driver
if driver is None:
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.maximize_window()
yield driver
driver.quit()
return driver def _capture_screenshot():
"""截图"""
return driver.get_screenshot_as_base64()

7、执行脚本

主要用于调用测试用例脚本,示例代码如下:

# -*- coding: utf-8 -*-
"""
@Time : 2022/12/10 18:04
@Auth : 软件测试君
@File :RunTestCase.py
@IDE :PyCharm
@Motto:ABC(Always Be Coding)
"""
import sys import pytest from config.conf import ROOT_DIR, HTML_NAME def main():
if ROOT_DIR not in sys.path:
sys.path.append(ROOT_DIR)
# 执行用例
args = ['--html=' + './report/' + HTML_NAME]
pytest.main(args) if __name__ == '__main__':
main()

8、测试效果

用例执行效果:

测试报告:

总结

其实写框架并不难,掌握核心思路,实现起来就会变得容易很多,与语言无关哦(因为我是Java党)。

关于API及很多细节部分,没做详细处理和封装,这里笔者仅仅是提供思路,感兴趣的同学,可自行去尝试进行进一步扩展,如想要源代码的同学可以文末留言或者加我好友领取哦。

Selenium4+Python3系列(十二) - 测试框架的设计与开发的更多相关文章

  1. SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据

    原文:SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Se ...

  2. Alamofire源码解读系列(十二)之请求(Request)

    本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...

  3. struts2官方 中文教程 系列十二:控制标签

    介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项 ...

  4. 爬虫系列(十二) selenium的基本使用

    一.selenium 简介 随着网络技术的发展,目前大部分网站都采用动态加载技术,常见的有 JavaScript 动态渲染和 Ajax 动态加载 对于爬取这些网站,一般有两种思路: 分析 Ajax 请 ...

  5. Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】

    2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...

  6. 【小梅哥FPGA进阶教程】第十二章 数字密码锁设计

    十二.数字密码锁设计 本文由山东大学研友袁卓贡献,特此感谢 实验目的 实现数字密码锁设计,要求矩阵按键输出且数码管显示输入密码,密码输入正确与否均会有相应标志信号产生. 实验平台 芯航线FPGA核心板 ...

  7. 专题开发十二:JEECG微云高速开发平台-基础用户权限

      专题开发十二:JEECG微云高速开发平台-基础用户权限 11.3.4自己定义button权限 Jeecg中.眼下button权限设置,是通过对平台自己封装的button标签(<t:dgFun ...

  8. 学习ASP.NET Core Razor 编程系列十二——在页面中增加校验

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  9. SpringBoot系列(十二)过滤器配置详解

    SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...

  10. Selenium4+Python3系列(五) - 多窗口处理之句柄切换

    写在前面 感觉到很惭愧呀,因为居然在Selenium+Java系列中没有写过多窗口处理及句柄切换的文章,不过也无妨,不管什么语言,其思路是一样的,下面我们来演示,使用python语言来实现窗口句柄的切 ...

随机推荐

  1. Java复制Word文档

    Microsoft Word 提供了许多易于使用的文档操作工具,同时也提供了丰富的功能集供创建复杂的文档使用.在使用的时候,你可能需要复制一个文档里面的内容到另一个文档.本文介绍使用Spire.Doc ...

  2. 请推荐下比较适合中小企业的ERP系统,如odoo,除前期开发和不定期完善,有没有其他固定月费或年费?

    odoo的话你自己就可以下载开源的安装使用的啊,如果你要别人帮你开发和完善做技术服务的话一般都还是要年费的,主要是因为要帮你做维护或修bug什么的,自己能搞定的话自然不需要的哦.只是odoo使用的是p ...

  3. CentOS 7 下安装 MySQL 8.x

    CentOS 7 下安装 MySQL 8.x 作者:Grey 原文地址: 博客园:CentOS 7 下安装 MySQL 8.x CSDN:CentOS 7 下安装 MySQL 8.x 环境 CentO ...

  4. MatrixOne从入门到实战04——MatrixOne的连接和建表

    MatrixOne从入门到实战--MatrixOne的连接和建表 前景回顾 前几篇文章,为大家介绍了MatrixOne这个产品,以及编译.部署MatrixOne的服务. 直通车: MatrixOne从 ...

  5. 一键上手时下最火AI作画工具

    摘要:在华为云ModelArts上, 无需考虑计算资源.环境的搭建,就算不懂代码,也能按照教程案例,通过Stable Diffusion成为艺术大师. 本文分享自华为云社区<跟着华为云Model ...

  6. 安装notepad++ 安装Python Python环境变量的数值。怎样在notepad++上运行Python的代码

    文章目录 1.下载安装一个Python的编辑器notepad++,(我这里有现成的,也可以去网上搜很多) 2.安装python,(我这里有现成的,也可以去网上下载). 3.怎样彻底删除Python,有 ...

  7. 如何实现一个SQL解析器

    ​作者:vivo 互联网搜索团队- Deng Jie 一.背景 随着技术的不断的发展,在大数据领域出现了越来越多的技术框架.而为了降低大数据的学习成本和难度,越来越多的大数据技术和应用开始支持SQL进 ...

  8. 知识图谱-生物信息学-医学顶刊论文(Bioinformatics-2021)-MUFFIN:用于DDI预测的多尺度特征融合

    2.(2021.3.15)Bioinformatics-MUFFIN:用于DDI预测的多尺度特征融合 论文标题: MUFFIN: multi-scale feature fusion for drug ...

  9. SpringBoot报错: No identifier specified for entity: XXX.XXX.XXX.XXX

    今天练习的时候报错说是 : 没有为实体指定标识符 仔细看了实体类才发现忘记写了一些注解 用JPA写实体类时一些注解是必须的 @entity  标名本类是实体类 @table(name="表名 ...

  10. SQL生成脚本

    右键要生成脚本的数据库 选择task 选择Generate script 选择需要生成脚本的table.view.procedure