python-Selenium分布式多线程自动化测试系统
1. 测试系统简介
刚开始写自动化的时候网上也看过许多教程,但都显得不够灵活。后面经过多次改良写出个测试框架,可以将所有的断言、xpath、用户操作等信息全部写在excel里面,代码里面只是封装操作方式,这样就提高了易用性、可维护性,希望对有意转向自动化的人有一些帮助。此测试框架由seleniumGrid+python完成,使用fastdfs储存测试截图并将截图路径和测试用列保存到数据库,然后将测试结果以网页的形式发送邮箱。教程不适合初学者看,需要具备python、selenium的基础知识。
若是有不同的见解或者更好的方式请留言或者联系我QQ:98669918一起学习,一起进步。
欢迎转载,请著名出处
2. 资源准备
2.1 搭建windows虚拟机
安装虚拟机的教程网上很多,我就不重复了
windows系统下载地址:https://msdn.itellyou.cn/
找到操作系统,选择你自己需要的下载就行
windows虚拟机,需要安装java。
如果不想用虚拟机可以直接在本地测试,只是在测试期间电脑将不能使用,推荐使用分布式部署
2.2 下载并配置Python
2.21 windows配置python
官网下载python安装包:https://www.python.org/downloads/windows/ 安装即可
2.22 Linux配置python
Linux系统自带python,若无特殊需求不必重新安装
2.3 创建Mysql库、表
#创建数据库
create database pytest;
#创建测试结果存储表
create table TEST(
title varchar(255) NOT NULL COMMENT '用例标题',
checkpoints varchar(255) NOT NULL COMMENT '检查点',
expected_results varchar(255) NOT NULL COMMENT '预期结果',
state varchar(20) NOT NULL COMMENT '用例执行状态pass/fali',
--不要用date格式,可能存数据的时候会有问题
time varchar(50) NOT NULL COMMENT '用例启动时间',
image_path varchar(255) NOT NUll COMMENT '截图',
version varchar(50) NOT NULL COMMENT '用例版本',
project varchar(255) NOT NULL COMMENT '用例所属项目',
id INT(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id)
);
2.4 安装、配置FastDFS文件服务器
2.5 下载、配置selenium
官网下载:http://selenium-release.storage.googleapis.com/index.html
网盘下载:https://pan.baidu.com/s/1CnXXiEwTBYyywojlALAq9w 提取码 ndsa
我用的:selenium-server-standalone-3.9.1.jar
2.6 安装selenium
输入命令:pip3 install selenium 进行安装,pip3是python3的,python2使用pip
安装完成输入:pip3 show selenium查看版本信息
3. 配置及测试selenium-Grid
3.1 启动selenium服务
不论部署几台机器,主机和测试机必须可以ping通,不然会启动失败
在主机(编写测试代码的机器)输入
java -jar selenium-server-standalone-3.9.1.jar -role hub -port 4444
#4444是默认端口,也可以修改为其他端口
在测试机(虚拟机)输入
#10.0.203.39:4444 是你自己控制机(hub所在机器)的IP和端口
#port 5555 是测试机端口,选择一个未被使用的端口就可以
java -jar selenium-server-standalone-3.9.1.jar -role node -port 5555 -hub http://10.0.203.39:4444/grid/register
当主机控制台打印测试机IP时,就表示连接成功了
3.2 浏览器驱动
在进行测试之前需要在node机器安装对应的浏览器驱动
网盘下载:https://pan.baidu.com/s/1hIvvAwkU-cNbP22Lm3WRLQ 提取码 gy9h
下载后解压文件,在系统盘根目录创建driver文件夹,将其加入系统环境变量。然后吧浏览器驱动复制进去
3.3 编写测试代码
在编写正式的代码之前,先写一个demo试一下环境是否搭建好
# -*- coding: UTF-8 -*-
from selenium.webdriver import Remote
'''浏览器驱动封装'''
def browser(host,browserb):
dc = {'browserName': browserb}
driver = Remote(command_executor=host, desired_capabilities=dc)
return driver
if __name__ == "__main__":
'''输入node的ip和端口'''
#chrome
dr = browser('http://172.16.122.151:5555/wd/hub','chrome')
#firefox
#dr = browser('http://172.16.122.151:5555/wd/hub','firefox')
dr.get('http://www.baidu.com')
执行代码后,node机打开浏览器并访问百度,就代表环境搭建完成,若是第一次启动报错,就先手动去node机启动浏览器驱动(双击运行即可)
4 公用方法
目录说明:
|__Login
| |__LoginCase:登录测试用例
| |__LoginExcel : 获取Login测试数据
|__action:操作方法封装
|__fdfs.conf:fastdfs文件服务器配置
|__fdfs:文件上传
|__main:入口
|__Screenshot:截图
|__sql:储存测试数据
|__email:发送邮件
4.1 线程类
重写Thread线程类,以测试机数量为线程数量,做到多台机器同步测试.
先传递node机器IP和浏览器类型到WebDriver,然后在执行测试类
main.py
# -*- coding: UTF-8 -*-
import threading
from selenium.webdriver import Remote
from email import Email
from Login import LoginCase
'''浏览器驱动封装'''
def driver(host,browserb):
print(host,browserb)
dc = {'browserName': browserb}
driver = Remote(command_executor=host, desired_capabilities=dc)
return driver
#线程锁,多线程读取系统资源时要枷锁,不然可能会出现数据混乱的情况
threadLock = threading.Lock()
#线程类
class myThread(threading.Thread):
def __init__(self, name,host,browser):
threading.Thread.__init__(self)
self.name = name
self.host = host
self.browser = browser
def run(self):
threadLock.acquire()
case(self.host, self.browser)
threadLock.release()
def __del__(self):
print(self.name, " : END Thread!")
#node机器IP与浏览器
hosts = {
'chrome':'http://172.16.122.160:5555/wd/hub'
#'firefox':'http://172.16.122.154:5555/wd/hub'
}
#创建线程
threads = []
def Thread():
i =0
for browsers ,host in hosts.items():
#线程名称
thread = 'thread%s' % (i)
#传递node ip、浏览器类型到线程类
thread = myThread(thread,host,browsers)
threads.append(thread)
i = i + 1
#加入线程池
for t in threads:
t.start()
#等待所有线程结束
for t in threads:
t.join()
#执行测试
def case(host,browser):
#执行Login测试类
LoginCase.main(host,browser)
if __name__ == "__main__":
Thread()
Email()
4.2 封装操作方法
操作方法封装,后面的测试类直接传递参数调用即可,减少代码冗余
action.py
# -*- coding: UTF-8 -*-
from selenium.webdriver.common.by import By
#xpath是页面元素定位
#input 是用户输入的参数
# 打开URL
def open_url(dr, url):
dr.get(url)
# 输入内容
def type_input(dr, xpath, input):
dr.find_element(By.XPATH, xpath).send_keys(input)
# 点击
def type_submit(dr, xpath):
dr.find_element(By.XPATH, xpath).click()
# 获取文本
def get_text(dr, xpath):
text = dr.find_element(By.XPATH, xpath).text
return text
#获取弹出框 alert值
def get_alert(dr):
alert = dr.switch_to_alert()
text = alert.text
alert.accept()
return text
# 退出
def quit(dr):
dr.quit()
4.3 上传截图文件
上传截图文件到fast文件服务器并返回文件ID
# -*- coding: UTF-8 -*-
import fdfs_client
from fdfs_client.client import *
#上传截图到fastdfs
def fastdfs(file):
#配置文件路径
client_conf = get_tracker_conf('/root/PycharmProjects/SeleniumGrid/fdfs.conf')
client = Fdfs_client(client_conf)
#以图像名称上传
#imgs = client.upload_appender_by_filename(file)
#以文件流上传
imgs = client.upload_appender_by_buffer(bytes(file, encoding="utf8"),'jpg')
#接受文件ID
path = str(imgs['Remote file_id'],encoding="utf8")
#接受fdfs文件服务器地址
ip = str(imgs['Storage IP'],encoding="utf8")
#拼接文件地址
img = str('%s/%s' % (ip,path))
return img
4.4 截图
使用selenium截图功能,将图片以流的形式上传,并将测试结果、测试截图存到数据库
# -*- coding: UTF-8 -*-
from fdfs import fastdfs, datetime
from sql import sqlDriver
def io(dr,title,jcd,qwjg,ass):
nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
version = '1.0'
project = 'TEST'
#截图
#files = dr.get_screenshot_as_base64()
files = dr.get_screenshot_as_png()
#上传截图,并保存截图位置
img = fastdfs(files)
#存储测试结果
sql = "INSERT INTO TEST(title,checkpoints,expected_results,state,time,image_path,version,project) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s')" % (title,jcd,qwjg,ass,nowTime,img,version,project)
sqlDriver(sql)
4.5 数据库方法封装
链接数据库,执行sql语句,返回结果
# -*- coding: UTF-8 -*-
import pymysql
# 打开数据库连接
def sqlDriver(sql):
#数据库IP和密码换成你自己的
db = pymysql.connect("127.0.0.1","root","root","pytest" )
cu = db.cursor()
cu.execute(sql)
data = cu.fetchall()
db.commit()
return data
4.5 fastdfa配置文件
connect_timeout = 2
network_timeout = 30
charset = UTF-8
#服务器端口
http.tracker_http_port = 80
http.anti_steal_token = no
#密钥
http.secret_key = FastDFS1234567890
#服务器IP,端口默认22122
tracker_server =127.0.0.1:22122
4.6 发送测试结果
email.py
# -*- coding: UTF-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.header import Header
#第三方邮件服务
mail_host = "smtp.yeah.net"
mail_user = "123456@yeah.net"
#这个地方不是邮箱登录密码,是授权码,不同的厂商有不同的设置方式
mail_pass = "123456"
# 发送邮件地址
sender = '1234@yeah.net'
# 接收邮件地址
receivers = ['1234@yeah.net']
#主题,我这里发的主题是一个链接,点击之后进入网页展示页面
mail_msg = """
<p><a href="http://127.0.0.1/Test">测试报告</a></p>
"""
message = MIMEText(mail_msg, 'html', 'utf-8')
message['From'] = Header('测试','utf-8')
message['To'] = Header('测试','utf-8')
subject = '测试报告'
message['Subject'] = Header(subject, 'utf-8')
def Email():
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print("邮件发送成功")
except smtplib.SMTPException:
print("Error: 无法发送邮件")
5. 测试类
5.1 Login数据获取
获取excel测试数据,存到list
List名称随意,要和excel对应起来
LoginExcel.py
# -*- coding: UTF-8 -*-
import xlrd
#标题
title = {}
#检查点
jcd = {}
#期望结果
qwjg = {}
#用户名
user = {}
#密码
passwd = {}
#断言内容
ass = {}
#用户名xpath
userxpath = {}
#密码xpath
passxpath = {}
#断言xpath
assxpath={}
#登录按钮xpath
sub = {}
#断言类型,text,alert
ass_type={}
def excel(s):
#excel路径
file_path = '/root/test.xlsx'
#打开excel
ex = xlrd.open_workbook(file_path)
#选取Sheet,0表示第一个
sheet = ex.sheets()[s]
#获取总行数
nors = sheet.nrows
#总列数
cols = sheet.ncols
#读取excel每行内容
for j in range(nors):
#跳过第一行,一般第一行都是标题,
if j == 0:
continue
#存到List
title[j-1] = sheet.cell_value(j, 0)
jcd[j-1] = sheet.cell_value(j, 1)
qwjg[j-1] = sheet.cell_value(j, 2)
user[j-1] = sheet.cell_value(j, 3)
passwd[j-1] = sheet.cell_value(j, 4)
ass[j-1] = sheet.cell_value(j, 5)
userxpath[j-1] = sheet.cell_value(j, 6)
passxpath[j-1] = sheet.cell_value(j, 7)
assxpath[j-1] = sheet.cell_value(j, 8)
sub[j-1] = sheet.cell_value(j, 9)
ass_type[j - 1] = sheet.cell_value(j, 10)
return nors
#获取数据
def get_title(i):
return title[i]
def get_jcd(i):
return jcd[i]
def get_qwjg(i):
return qwjg[i]
def get_user(i):
return user[i]
def get_passwd(i):
return passwd[i]
def get_ass(i):
return ass[i]
def get_userxpath(i):
return userxpath[i]
def get_passxpath(i):
return passxpath[i]
def get_assxpath(i):
return assxpath[i]
def get_sub(i):
return sub[i]
def get_asstype(i):
return ass_type[i]
5.2 测试用列封装
根据登录页面内容,测试类思路如下:
1. 根据excel提供的xpath获取用户名文本框,并输入excel提供的用户名
2. 根据excel提供的xpath获取密码文本框,并输入excel提供的密码
3. 根据excel提供的xpath获取登录按钮并点击
4. 根据excel提供的断言类型判断应该用那种方式获取断言文本
5. 根据excel提供的xpath获取断言内容
6. 比较页面获取的断言内容是否与excel存放的断言内容一致
7. 页面截图并将测试信息存到数据库
LoginCase.py
# -*- coding: UTF-8 -*-
from time import sleep
from Login.LoginExcel import *
from Screenshot import io
from action import *
from main import driver
def main(host, browser):
global text
#登录页面地址
url = "http://www.sightfuture.cn:81/zentao/user-login.html"
#获取excel总行数
nor = excel(0)
#遍历excel并执行测试,一般excel第一行是标题,不是测试数据。所以nor-1
for i in range(nor-1):
# WebDriver
dr = driver(host, browser)
# 打开网页
open_url(dr, url)
#输入用户名
type_input(dr,get_userxpath(i),get_user(i))
#输入密码
type_input(dr, get_passxpath(i),get_passwd(i))
#点击登录
type_submit(dr, get_sub(i))
#获取断言类型
type = get_asstype(i)
#根据断言类型判断怎样获取断言文本
if(type == 'text'):
# 获取断言文本
text = get_text(dr, get_assxpath(i))
elif(type =='alert'):
#获取alert弹出文本
text = get_alert(dr)
#比较获取的断言文本是否为excel储存的文本
if(text == get_ass(i)):
status = 'pass'
else:
status = 'fail'
print("断言内容:%s ,断言结果 :%s" % (text,status))
#截图并将测试信息存到数据库
#截图延迟
sleep(1)
io(dr,get_title(i),get_jcd(i),get_qwjg(i),status)
# 退出浏览器
dr.quit()
到此测试代码部分编写完成,虽然只有Login,但其他页面也可以按照这种模式进行设计,一次设计永久使用,以后只需要不断维护excel即可,减小测试开发时间。
接下来运行一次看下效果:
可以看到两次都是测试通过,说明登录功能正常
到此处测试部分代码已经编写完成,剩下的前端展示页面自行解决,只需要展示数据内容和图片即可
5.3 代码下载
完整代码下载路径:
https://pan.baidu.com/s/1aTED0OFo9qmZop5OEsRZ4g 提取码 c6fa