Selenium+Python做web端自动化测试框架与实例详解教程

最近受到万点暴击,由于公司业务出现问题,工作任务没那么繁重,有时间摸索selenium+python自动化测试,结合网上查到的资料自己编写出适合web自动化测试的框架,由于本人也是刚刚开始学习python,这套自动化框架目前已经基本完成了所以总结下编写的得失,便于以后回顾温习,有许多不足的的地方,也遇到了各种奇葩问题,希望大神们多多指教

首先我们要了解什么是自动化测试,简单的说编写代码、脚本,让软件自动运行,发现缺陷,代替部分的手工测试。了解了自动化测试后,我们要清楚一个框架需要分那些模块:

上图的框架适合大多数的自动化测试,比如web UI  、接口自动化测试都可以采用,如大佬有好的方法请多多指教,简单说明下每个模块:

  • common:存放一些共通的方法
  • data:存放一些文件信息
  • logs:存放程序中写入的日志信息
  • picture:存放程序中截图文件信息
  • report:存放测试报告
  • test_case:存放编写具体的测试用例
  • conf.ini、readconf.py:存放编写的配置信息

下面就具体介绍每个模块的内容:conf.ini主要存放一些不会轻易改变的信息,编写的代码如下:

[DATABASE]
host = 127.0.0.1
username = root
password = root
port = 3306
database = cai_test[HTTP]
# 接口的url
baseurl = http://xx.xxxx.xx
port = 8080
timeout = 1.0
readconf.py文件主要用于读取conf.ini中的数据信息
# *_*coding:utf-8 *_*
__author__ = "Test Lu"
import os,codecs
import configparser
prodir = os.path.dirname(os.path.abspath(__file__))
conf_prodir = os.path.join(prodir,'conf.ini')
class Read_conf():def __init__(self):with open(conf_prodir) as fd:data = fd.read()#清空文件信息if data[:3] ==codecs.BOM_UTF8:data = data[3:]file = codecs.open(conf_prodir,'w')file.write(data)file.close()self.cf = configparser.ConfigParser()self.cf.read(conf_prodir)def  get_http(self,name):value = self.cf.get("HTTP",name)return valuedef get_db(self,name):return self.cf.get("DATABASE",name)
这里需要注意,python3.0以上版本与python2.7版本import configparser的方法有一些区别
读取一些配置文集就介绍完了,下面就说说common包下的公共文件

现在就从上往下结束吧!common主要是封装的一些定位元素的方法:

# *_*coding:utf-8 *_*
__author__ = "Test Lu"
from selenium import webdriver
import time,os
import common.config
# from common.logs import MyLog
project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class Comm(object):def __init__(self,driver):self.driver = driver# self.driver = webdriver.Firefox()self.driver = webdriver.Chrome()self.driver.maximize_window()def open_url(self,url):self.driver.get(url)self.driver.implicitly_wait(30)# selenium 定位方法def locate_element(self,loatetype,value):if (loatetype == 'id'):el = self.driver.find_element_by_id(value)if (loatetype == 'name'):el = self.driver.find_element_by_name(value)if (loatetype == 'class_name'):el = self.driver.find_element_by_class_name(value)if (loatetype == 'tag_name'):el = self.driver.find_elements_by_tag_name(value)if (loatetype == 'link'):el = self.driver.find_element_by_link_text(value)if (loatetype == 'css'):el = self.driver.find_element_by_css_selector(value)if (loatetype == 'partial_link'):el = self.driver.find_element_by_partial_link_text(value)if (loatetype == 'xpath'):el = self.driver.find_element_by_xpath(value)return el if el else None# selenium 点击def click(self,loatetype,value):self.locate_element(loatetype,value).click()#selenium 输入def input_data(self,loatetype,value,data):self.locate_element(loatetype,value).send_keys(data)#获取定位到的指定元素def get_text(self, loatetype, value):return self.locate_element(loatetype, value).text# 获取标签属性def get_attr(self, loatetype, value, attr):return self.locate_element(loatetype, value).get_attribute(attr)# 页面截图def sc_shot(self,id):for filename in os.listdir(os.path.dirname(os.getcwd())) :if filename == 'picture':breakelse:os.mkdir(os.path.dirname(os.getcwd()) + '/picture/')photo = self.driver.get_screenshot_as_file(project_dir +  '/picture/'+ str(id) + str('_') + time.strftime("%Y-%m-%d-%H-%M-%S") + '.png')return photodef __del__(self):time.sleep(2)self.driver.close()self.driver.quit()
下面介绍下,config文件主要用于读取文件中的信息:
import os,xlrd
from common.logs import MyLog
from xml.etree import ElementTree as ElementTree
mylogger = MyLog.get_log()
project_dir = os.path.dirname(os.getcwd())def user_Add():'''excel文件中读取用户登录信息'''with xlrd.open_workbook(project_dir+'/data/test_data.xlsx') as files:table_user = files.sheet_by_name('userdata')try:username = str(int(table_user.cell(1,0).value))except:username = str(table_user.cell(1,0).value)try:passwd = str(int(table_user.cell(1,1).value))except:passwd = str(table_user.cell(1,1).value)try:check = str(int(table_user.cell(1, 2).value))except Exception:check = str(table_user.cell(1, 2).value)table_url = files.sheet_by_name('base_url')base_url = str(table_url.cell(1,0).value)return (username,passwd,base_url,check)
#从xml文件中读取信息,定义全局一个字典来存取xml读出的信息
database={}
def set_read_xml():sql_path = os.path.join(project_dir,'data','SQL.xml')data =ElementTree.parse(sql_path)for db in data.findall('database'):name = db.get('name')table = {}for tb in db.getchildren():table_name = tb.get("name")sql = {}for data in tb.getchildren():sql_id = data.get("id")sql[sql_id] = data.texttable[table_name] = sqldatabase[name] = tablemylogger.info("读取的xml文件的信息%s" %database)
def get_sql_sen(database_name,table_name,sql_id):set_read_xml()db = database.get(database_name).get(table_name)if db.get(sql_id):sql = db.get(sql_id).strip()mylogger.info("返回sql语句信息%s" % sql)return sqlelse:mylogger.info("查下的信息为空,传递的参数有误!数据库名称:【%s】,表信息【%s】,查询的id【%s】"%(database_name,table_name,sql_id))
接着介绍最简单的日志logs.py模块:
# logging模块支持我们自定义封装一个新日志类
import logging,time
import os.path
class Logger(object):def __init__(self, logger,cases="./"):       self.logger = logging.getLogger(logger)self.logger.setLevel(logging.DEBUG)self.cases = cases# 创建一个handler,用于写入日志文件for filename in os.listdir(os.path.dirname(os.getcwd())):if filename == "logs":breakelse:os.mkdir(os.path.dirname(os.getcwd())+'/logs')rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))log_path = os.path.dirname(os.getcwd()) + '/logs/'  log_name = log_path + rq + '.log'  # 文件名# 将日志写入磁盘fh = logging.FileHandler(log_name)fh.setLevel(logging.INFO)# 创建一个handler,用于输出到控制台ch = logging.StreamHandler()ch.setLevel(logging.INFO)# 定义handler的输出格式formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')fh.setFormatter(formatter)ch.setFormatter(formatter)# 给logger添加handlerself.logger.addHandler(fh)self.logger.addHandler(ch)def getlog(self):return self.logger
common模块最后一个是test_runner.py这个方法主要是用来执行全部的测试用例
import time,HTMLTestRunner
import unittest
from common.config import *
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),os.pardir))
class TestRunner(object):''' 执行测试用例 '''def __init__(self, cases="../",title="Auto Test Report",description="Test case execution"):self.cases = casesself.title = titleself.des = descriptiondef run(self):for filename in os.listdir(project_dir):if filename == "report":breakelse:os.mkdir(project_dir+'/report')# fp = open(project_dir+"/report/" + "report.html", 'wb')now = time.strftime("%Y-%m-%d_%H_%M_%S")# fp = open(project_dir+"/report/"+"result.html", 'wb')fp = open(project_dir+"/report/"+ now +"result.html", 'wb')tests =  unittest.defaultTestLoader.discover(self.cases,pattern='test*.py',top_level_dir=None)runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=self.title, description=self.des)runner.run(tests)fp.close()
以上就是common公共模块所有的模块,简单说下在写这些公共模块时,出现了各种问题,特别是读取xml文件的,唉!对于一个python的小白真是心酸啊!接着说下db模块的内容,db模块主要是读取sql语句以及返回对应的值!
import pymysql
import readconf
import common.config as conf
readconf_conf = readconf.Read_conf()host = readconf_conf.get_db("host")
username = readconf_conf.get_db("username")
password = readconf_conf.get_db("password")
port = readconf_conf.get_db("port")
database = readconf_conf.get_db("database")
config_db = {'host': str(host),'user': username,'password': password,'port': int(port),'db': database
}
class Mysql_DB():def __init__(self):'''初始化数据库'''self.db = Noneself.cursor = Nonedef connect_db(self):'''创建连接数据库'''try:self.db = pymysql.connect(**config_db)#创建游标位置self.cursor = self.db.cursor()# print("链接数据库成功")conf.mylogger.info("链接IP为%s的%s数据库成功" %(host,database))except ConnectionError as ex:conf.mylogger.error(ex)def get_sql_result(self,sql,params,state):self.connect_db()try:self.cursor.execute(sql, params)self.db.commit()# return self.cursorexcept ConnectionError as ex:self.db.rollback()if state==0:return self.cursor.fetchone()else:return self.cursor.fetchall()def close_db(self):print("关闭数据库")conf.mylogger.info("关闭数据库")self.db.close()
刚开始写db模块是一直对字典模块的信息怎样传递到数据链接的模块,进过网上查询好些资料才彻底解决,对自己来说也是一种进步,哈哈,下面说下自己踩的坑,帮助自己以后学习**config_db把字典变成关键字参数传递,
下面举例说明下:
如果kwargs={'a':1,'b':2,'c':3}那么**kwargs这个等价为test(a=1,b=2,c=3)是不是很简单!哈哈以上就是框架的主要模块,其他的模块每个项目与每个系统都不一样,在这里就是列举出来了,因为就算写出来大家也不能复用,下面就给大家看看小白还有哪些模块

看下了下data模块下的xml模块大家可能用的到,就给大家贴出来吧!因为ui测试主要就用到select与delete语句,所以也没有写多么复杂的sql语句

<?xml version="1.0" encoding="utf-8" ?>
<data><database name="database_member"><table name="table_member"><sql id="select_member">select * from user where real_name=%s</sql><sql id="select_member_one">select mobile from user where mobile=%s</sql><sql id="delete_member">delete from user where mobile=%s</sql><sql id="insert_member">insert into user(id) value(%s)</sql><sql id="update_member">uodate user set real_name = %s where uuid=%s</sql></table></database>
</data>
下面介绍下其他模块的内容:test_data.xlsx文件主要是存放一些用户信息,以及url信息,这样修改用户信息与url信息就不要修改代码方便以后操作!logs是在代码运行时候产生的日志信息,picture是存放图片信息,report存放输入的报告信息,
test_case是编写用户的模块需要所有的用例名称都要以test开头来命名哦,这是因为unittest在进行测试时会自动匹配test_case文件夹下面所有test开头的.py文件

Python接口自动化测试零基础入门到精通(2023最新版)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023网络安全产业图谱

1. 前言 2023年7月10日&#xff0c;嘶吼安全产业研究院联合国家网络安全产业园区&#xff08;通州园&#xff09;正式发布《嘶吼2023网络安全产业图谱》。 嘶吼安全产业研究院根据当前网络安全发展规划与趋势发布《嘶吼2023网络安全产业图谱》调研&#xff0c;旨在进一步了解…

使用Kafka、Flink、Druid构建实时数据系统架构

1. 背景 对于很多数据团队来说&#xff0c;要满足实时需求并不容易。为什么&#xff1f;因为作流程&#xff08;数据采集、预处理、分析、结果保存&#xff09;涉及大量等待。等待数据发送到 ETL 工具&#xff0c;等待数据批量处理&#xff0c;等待数据加载到数据仓库中&#…

k8s中pod的hostport端口突然无法访问故障处理

故障背景&#xff1a; 租户告知生产环境的sftp突然无法访问了&#xff0c;登录环境查看sftp服务运行都是正常的&#xff0c;访问sftp的hostport端口确实不通。 故障处理过程 既然访问不通那就先给服务做个全面检查&#xff0c;看看哪里出了问题&#xff0c;看下sftp日志&#…

Maven——Maven使用基础

1、安装目录分析 1.1、环境变量MAVEN_HOME 环境变量指向Maven的安装目录&#xff0c;如下图所示&#xff1a; 下面看一下该目录的结构和内容&#xff1a; bin&#xff1a;该目录包含了mvn运行的脚本&#xff0c;这些脚本用来配置Java命令&#xff0c;准备好classpath和相关…

Java —— 泛型

目录 1. 什么是泛型 2. 泛型背景及其语法规则 3. 泛型类的使用 3.1 语法 3.2 示例 3.3 类型推导(Type Inference) 4. 裸类型(Raw Type) 4.1 说明 5. 泛型如何编译的 5.1 擦除机制 5.2 为什么不能实例化泛型类型数组 6. 泛型的上界 6.1 上界语法产生的背景 6.2 语法 6.3 示例 6.…

服务器bash进程占用cpu过多疑似中挖矿病毒记录

发现过程 因为我有使用conky的习惯&#xff0c;也就是在桌面上会显示cpu和内存的占用情况&#xff0c;由于服务器不止我一个人使用&#xff0c;最近发现好几次我同学的账户下的bash进程占用特别多&#xff0c;问了他之后&#xff0c;他也说他几次都是没有使用过bash相关服务&a…

八股文-如何理解Java中的多态

什么是多态&#xff1f; 多态是面向对象编程的一个重要概念&#xff0c;它允许一个对象以不同的形式表现。也就是说&#xff0c;在父类中定义的属性和方法&#xff0c;在子类继承后&#xff0c;可以有不同的数据类型或表现出不同的行为。这可以使得同一个属性或方法&#xff0…

41.0/查询/sql注入安全问题以及解决方式。

41.1. 回顾 1. jdbc&#xff1a;[java database connection] java连接数据库 2. 完成了增删改操作。 [1]加载驱动。Class.forName("com.mysql.cj.jdbc.Driver"); [2]获取连接对象: Connection connDriverManager.getConnection(url,user,pass); url: jdb…

java+python农村集体产权管理系统php+vue

注册、登陆该系统根据操作权限的不同分为管理员和用户两种&#xff0c;新用户在登陆前要进行用户注册&#xff0c;注册完成后方可进行登陆。 本次设计的关键问题处理&#xff0c;主要有如下几点&#xff1a; (1&#xff09;本次开发&#xff0c;采用主流Thinkphp框架进行开发&a…

香港科技大学广州|智能制造学域博士招生宣讲会—华中科技大学专场

时间&#xff1a;2023年12月08日&#xff08;星期五&#xff09;15:00 地点&#xff1a;华中科技大学大学生活动中心A座603 报名链接&#xff1a;https://www.wjx.top/vm/mmukLPC.aspx# 宣讲嘉宾&#xff1a; 胡鹏程 副教授 https://facultyprofiles.hkust-gz.edu.cn/faculty-…

【区块链】产品经理的NFT初探

常见的FT如比特币&#xff08;BTC&#xff09;&#xff0c;以太币&#xff08;ETH&#xff09;等&#xff0c;两个代币之间是完全可替换的。而NFT具有唯一性&#xff0c;不可以互相替换。本文作者对NET的发展现状、相关协议、应用场景等方面进行了分析&#xff0c;一起来看一下…

数字阅读用户规模持续增长 5.3亿人享受数字化阅读便利

近日,鲁迅长孙周令飞在接受采访时表示,自己“现在90%的时间刷视频,10%的时间看书”,引发网友热议。不少网友表示,鲁迅的孙子都花90%的时间刷视频,难怪现在没人看书了,其实这并不奇怪,也并不表明没人看书,而是读屏与读书并重的时代,纸质阅读与数字阅读共同构成了日常的阅读模式。…