Python + selenium 自动化框架介绍

一、简介

在调研公司的web自动化测试过程中,我自己也简单的写了一个Python + selenium的自动化测试框架,该框架适合web前端自动化测试。实现原理是通过获取web元素的坐标地址(id、class、xpath等),然后对坐标对应的属性进行相应的操作:click、input、link等,从而实现前后端数据响应和页面跳转变化,最后通过断言的方式来check当前测试结果。

二、框架介绍

1、框架树状图

2、框架功能

如上图所示,该框架一共有四大部分:config、test、sum_excute_Scripts.py、test_000@@@.py。下面简单的对这四个模块功能讲述一下。

config:里面放入的是基本配置信息,如config.ini:

# this is config file, only store browser type and server URL
[browserType]
#browserName=Firefox
browserName=Chrome
#browserName = IE
[login]
userid = r@@@@
password = @@@@
#userid = audi
#password = audi123
[testServer]
#url = http://s@@@@@.com:88/pp/Timesheets/Submit/Index
url = http://s@@@@@.com:88/pp/Home/Login
#url = http://s@@@@@@.com:16310
[add_info]
local_tel = 12345678901
ids = 300823@@@@@@@@
[get_path]
file_path = C:\Users\@@@@@PycharmProjects\ACI_Auto\beta1\config\config.ini
#项目根目录路径
path = C:\Users\@@@@PycharmProjects\ACI_Auto_beta2
上面的文件内容主要选择测试浏览器、登录信息、测试web地址、web内配置参数及相关路径,其实这些还可以细分,这边暂不拆分了。
test:我们重点看一下该层级下的内容:
color_Change.py 文件主要是对输出的excel统计表单元格做颜色匹配,fail的case是红色,pass的case是绿色
#!/usr/bin/env python
#coding:utf-8
import xlwt
__author__ = 'dingrui'
def color():
# Create the Pattern
pattern = xlwt.Pattern()
# May be: NO_PATTERN, SOLID_PATTERN, or 0x00 through 0x12
pattern.pattern = xlwt.Pattern.SOLID_PATTERN
# May be: 8 through 63. 0 = Black, 1 = White, 2 = Red, 3 = Green, 4 = Blue, 5 = Yellow, 6 = Magenta, 7 = Cyan, 16 = Maroon,
# 17 = Dark Green, 18 = Dark Blue, 19 = Dark Yellow , almost brown), 20 = Dark Magenta, 21 = Teal, 22 = Light Gray, 23 = Dark Gray,
pattern.pattern_fore_colour = 2
# Create the Pattern
style = xlwt.XFStyle()
# Add Pattern to Style
style.pattern = pattern
return style
comm_path.py文件主要用来访问config下config.ini中的配置,通过对象的方式在其它文件中调用。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import ConfigParser
def public_path():
# 获取config中的config.ini公用参数
config = ConfigParser.ConfigParser()
file_path ="C:\Users\@@@@PycharmProjects\ACI_Auto_beta2\config\config.ini"
config.read(file_path)
return config
Email_Config.py 用来配置email发送接收的参数
#!/usr/bin/env python
# -*-coding:utf-8-*-
__author__ = 'dingrui'
# 测试邮箱配置
Smtp_Server = 'smtp.163.com'
Smtp_Sender = ‘@@@@@@163.com'
Smtp_Sender_Password = ‘e@@@@@@‘
#Smtp_Receiver = [‘@@@@@mathartsys.com']
Smtp_Receiver = [‘1@@@@@@163.com']
email_Conn.py 用来实现邮件的发送测试log的功能
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import os
import sys
import smtplib
from test import Email_Congfig
from test.zip_log import zip_ya
import time
reload(sys)
sys.setdefaultencoding('utf8')
from email.mime.text import MIMEText
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
def send_Mail():
try:
smtp = smtplib.SMTP(Email_Congfig.Smtp_Server, 25)
sender = Email_Congfig.Smtp_Sender
password = Email_Congfig.Smtp_Sender_Password
receiver = Email_Congfig.Smtp_Receiver
smtp.login(sender, password)
msg = MIMEMultipart()
now = time.strftime("%Y-%m-%d-%H_%M_%S")
# 编写html类型的邮件正文,MIMEtext()用于定义邮件正文
# 发送正文
text = MIMEText('你好!', 'text', 'utf-8')
# 定义邮件正文标题
text['Subject'] = Header('XXXXUI自动化测试报告', 'utf-8')
text["Accept-Language"] = "zh-CN"
text["Accept-Charset"] = "ISO-8859-1,utf-8"
msg.attach(text)
# ---这是附件部分---
# zip类型附件,MIMEApplication不管什么类型都可以用
zip_pathname = zip_ya()
part = MIMEApplication(open(zip_pathname, 'rb').read())
part.add_header('Content-Disposition', 'attachment', filename=zip_pathname)
msg.attach(part)
# 发送附件
# Header()用于定义邮件主题,主题加上时间,是为了防止主题重复,主题重复,发送太过频繁,邮件会发送不出去。
msg['Subject'] = Header('[执行结果:' + 'XXXXUI自动化测试报告' + now, 'utf-8')
# 定义发件人,如果不写,发件人为空
msg['From'] = sender
# 定义收件人,如果不写,收件人为空
msg['To'] = ",".join(receiver)
tmp = smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()
return True
except smtplib.SMTPException as e:
return False
logger.py 用来实现对断点和每一步测试的结果进行记录,将打印的log保存到/Logs的当前case文件下。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import logging
import os.path
import time
import sys
reload(sys)
sys.setdefaultencoding('utf8')
class Logger():
def __init__(self, logger, path):
"""
指定保存日志的文件路径,日志级别,以及调用文件
将日志存入到指定的文件中
:param logger:
"""
# 创建一个logger
self.logger = logging.getLogger(logger)
self.logger.setLevel(logging.DEBUG)
log_path_excel = os.path.dirname(os.getcwd()) + '/Logs/'
print("log_path_excel:", log_path_excel)
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
# 创建一个handler,用于写入日志文件
file_case_name = log_path_excel + path
print("file_case_name:", file_case_name)
os.mkdir(file_case_name, 0777)
self.log_path = file_case_name
log_path = self.log_path
self.log_name = log_path + '/' + rq + path + '.log'
log_name = self.log_name
print("log_name:", log_name)
fh = logging.FileHandler(log_name)
#调高log等级
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
fh.close()
ch.close()
def getlog(self):
return self.logger
read_Excel.py 该方法主要作用是要来新建一个excel,创建记录格式,为记录结果提供准备。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import time
import xlrd
import xlwt
from xlutils.copy import copy
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
excel_Name = "D:\CI" + rq + ".xlsx"
def new_excel():
wt = xlwt.Workbook()
ws_1 = wt.add_sheet('Test_Record'.decode("utf-8"))
first_col = ws_1.col(0)
four_col = ws_1.col(4)
six_col = ws_1.col(6)
first_col.width = 300 * 20
four_col.width = 300 * 40
six_col.width = 1000 * 20
ws_1.write(0, 0, 'Case_Name')
ws_1.write(0, 2, 'Result')
ws_1.write(0, 4, 'Cause')
ws_1.write(0, 6, 'Log_Path')
ws_2 = wt.add_sheet("results".decode("utf-8"))
ws_2.write(0, 0, "case_sum")
ws_2.write(0, 1, "fail_num")
ws_2.write(0, 2, "pass_num")
ws_2.write(0, 3, "rate")
wt.save(excel_Name)
#return wt, ws_1
def read_excel():
# 打开指定路径中的xls文件,得到book对象
#xls_file = "D:\Case_Traces_1.xlsx"
book = xlrd.open_workbook(excel_Name, formatting_info=True) # 得到Excel文件的book对象,实例化对象
wb = copy(book)
wt = wb.get_sheet(0)
return book, wb, wt
Result_Excel.py 通过read_Excel.py的调用,将每一条case结果记录起fail/pass保存在excel中,用以反馈。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import os
from test.read_Excel import *
from test.comm_path import public_path
# 获取config中的config.ini公用参数
config = public_path()
path = config.get("get_path", "path")
def readFilename(path):
for root, dirs, files in os.walk(path):
return root, dirs, files
def startfind_count(root, dirs, files):
j = 0
count = []
for ii in files:
if ii.startswith('test_') and ii.endswith('.py'):
j = j + 1
count.append(j)
else:
continue
return count
def startfind(root, dirs, files):
book, wb, wt = read_excel()
i = 0
for ii in files:
if ii.startswith('test_') and ii.endswith('.py'):
case_Name = ii.split('.py')[0]
i = i + 1
wt.write(i, 0, case_Name)
else:
continue
wb.save(excel_Name)
zip_log.py 主要的作用是压缩Logs文件中的测试log日志,主要使用在邮件附件中添加模块。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import os
import tarfile
import zipfile
import time
def zip_ya():
startdir = "C:\Users\mathartsys\PycharmProjects\Logs\\" #要压缩的文件夹路径
nowdate = time.strftime("%Y-%m-%d-%H_%M_%S")
file_news = startdir + nowdate + '.zip' # 压缩后文件夹的名字
z = zipfile.ZipFile(file_news, 'w', zipfile.ZIP_DEFLATED) #参数一:文件夹名
zip_pathname = z.filename
for dirpath, dirnames, filenames in os.walk(startdir):
fpath = dirpath.replace(startdir, '') #这一句很重要,不replace的话,就从根目录开始复制
fpath = fpath and fpath + os.sep or ''#这句话理解我也点郁闷,实现当前文件夹以及包含的所有文件的压缩
for filename in filenames:
z.write(os.path.join(dirpath, filename), fpath+filename)
z.close()
return zip_pathname
sum_excute_Scripts.py 该脚本文件就是用来执行的,启动该脚本,首先判断Logs是否被清空,然后遍历所有case,执行,统计通过率,该脚本还有待拆分优化。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import unittest
import ConfigParser
import os
import xlrd
import xlwt
from xlutils.copy import copy
import shutil
from test.Result_Excel import *
from test.read_Excel import *
from test.email_Conn import *
#新建本轮测试的excel
new_excel()
files, dirs, root = readFilename(path)
startfind(files, dirs, root)
# 用例路径
case_path = os.getcwd()
#删除log日志里面之前的所有文件
def clear_logfiles():
delList = []
delDir = “C:\@@@@@\PycharmProjects\Logs\\"
delList = os.listdir(delDir )
for f in delList:
filePath = os.path.join( delDir, f )
if os.path.isfile(filePath):
os.remove(filePath)
print filePath + " was removed!"
elif os.path.isdir(filePath):
shutil.rmtree(filePath,True)
print "Directory: " + filePath +" was removed!"
# 报告存放路径
def all_case():
discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None)
return discover
def sum_excel_count():
book, wb, wt = read_excel()
sheet_names = book.sheet_names()
sheet0 = book.sheet_by_name(sheet_names[0])
wt1 = wb.get_sheet(1)
len_cols = len(sheet0.col_values(0))
cols_result = sheet0.col_values(2)
#记录总case数
#wt.write(len_cols, 0, len_cols-1)
global Fail_num
global Pass_num
Fail_num = 0
Pass_num = 0
for result in cols_result:
if result == "FAIL":
Fail_num = Fail_num + 1
elif result == "PASS":
Pass_num = Pass_num+1
else:
continue
wt1.write(1, 0, len_cols-1)
wt1.write(1, 1, Fail_num)
wt1.write(1, 2, Pass_num)
rate_num = '{:.2f}%'.format(float(Pass_num)/(float(len_cols)-1)*100)
wt1.write(1, 3, rate_num)
wb.save(excel_Name)
if __name__ == "__main__":
clear_logfiles()
runner = unittest.TextTestRunner()
runner.run(all_case())
sum_excel_count()
send_Mail()
test_@@@@.py这个就是具体脚本实现的过程,使用的是unittest单元测试技术。
#!/usr/bin/env python
#coding:utf-8
__author__ = 'dingrui'
import os
import sys
import unittest
reload(sys)
sys.setdefaultencoding('utf8')
from selenium import webdriver
from test.logger import Logger
from test.Result_Excel import *
from test.color_Change import *
# 获取config中的config.ini公用参数
config = public_path()
def Xpath_Name():
# 获取当前脚本的名称
path = os.path.basename(__file__)
Xpath = path.split(".")
path_Name = Xpath[0]
print("path_Name:",path_Name)
return path_Name
#获取log记录对象
mylogger = Logger(logger= __name__, path=Xpath_Name())
class Conn(unittest.TestCase):
def setUp(self):
#参数化选择浏览器
browser = config.get("browserType", "browserName")
mylogger.getlog().info("You had select %s browser." % browser)
#参数化选择地址、手机号、身份证号
self.url = config.get("testServer", "url")
url = self.url
mylogger.getlog().info("The test server id is: %s" % url)
self.local_tel = config.get("add_info", "local_tel")
local_tel = self.local_tel
mylogger.getlog().info("The test server local_tel is: %s" % local_tel)
self.ids = config.get("add_info", "ids")
ids = self.ids
mylogger.getlog().info("The test server ids is: %s" % ids)
#根据需求选择浏览器驱动
if browser == "Firefox":
self.driver = webdriver.Firefox()
mylogger.getlog().info("Starting firefox browser.")
elif browser == "Chrome":
self.driver = webdriver.Chrome()
mylogger.getlog().info("Starting Chrome browser.")
elif browser == "IE":
self.driver = webdriver.Ie()
mylogger.getlog().info("Starting IE browser.")
def test_search_in(self):
#给定标志位flag为true
flag = True
mylogger.getlog().info("启动浏览器")
driver = self.driver
mylogger.getlog().info("打开网页")
driver.get(self.url)
#输入用户名和密码并登录
self.userid = config.get("login", "userid")
userid = self.userid
self.password = config.get("login", "password")
password = self.password
driver.find_element_by_name("UserId").send_keys(userid)
mylogger.getlog().info("Input the UserId")
driver.find_element_by_name("Password").send_keys(password)
mylogger.getlog().info("Input the password")
driver.find_element_by_class_name("login_btn").click()
mylogger.getlog().info("click the buttom go in index.")
time.sleep(1)
#检查登录是否成功
try:
driver.find_element_by_link_text("个人视图")
mylogger.getlog().info("登录成功\n")
except:
flag = False
mylogger.getlog().info("登录失败\n")
#填写身份证、电话信息并保存
driver.find_element_by_link_text("个人视图").click()
time.sleep(1)
driver.find_element_by_link_text("我的设置").click()
local_tel = self.local_tel
ids = self.ids
driver.find_element_by_id("txtTelephone").send_keys(local_tel)
time.sleep(1)
driver.find_element_by_id("txtIdentityCard").send_keys(ids)
time.sleep(1)
driver.find_element_by_id("btnSaveInfo").click()
#开始检查添加是否成功
check_local_phone = driver.find_element_by_id("txtTelephone").get_attribute("value")
check_ids = driver.find_element_by_id("txtIdentityCard").get_attribute("value")
root, dirs, files = readFilename(path)
COUNT = startfind_count(root, dirs, files)
count = COUNT
if check_local_phone == local_tel and check_ids == ids:
mylogger.getlog().info("添加数据成功,该测试case通过")
self.book, self.wb, self.wt = read_excel()
wb = self.wb
wt = self.wt
wt.write(count[0], 2, "PASS")
wb.save(excel_Name)
else:
mylogger.getlog().info("添加数据失败,该测试case不通过")
self.style = color()
style = self.style
self.book, self.wb, self.wt = read_excel()
wb = self.wb
wt = self.wt
wt.write(count[0], 2, "FAIL", style)
log_name = mylogger.log_name
wt.write(count[0], 4, "add data fail", style)
wt.write(count[0], 6, "%s" % log_name, style)
wb.save(excel_Name)
def tearDown(self):
#清除测试数据,还原测试前的环境
self.driver.find_element_by_id("txtTelephone").clear()
print ('test data tel clear successful')
self.driver.find_element_by_id("txtIdentityCard").clear()
print ('test data id clear successful')
time.sleep(1)
self.driver.find_element_by_id("btnSaveInfo").click()
mylogger.getlog().info("该测试用例结束,关闭浏览器!\n")
self.driver.close()
#获取日志正文信息,写入到email中
log_name = mylogger.log_name
f = open(log_name, 'rb')
# 读取测试报告正文
mail_body = f.readlines()
f.close()
#send_Mail(self, mail_body)
if __name__ == "__main__":
unittest.main()