python+selenium自动化框架搭建

 

环境及使用软件信息

  1. python 3
  2. selenium 3.13.0
  3. xlrd 1.1.0
  4. chromedriver
  5. HTMLTestRunner
说明:
selenium/xlrd只需要再python环境下使用pip install 名称即可进行对应的安装。 安装完成后可使用pip list查看自己的安装列表信息。
chromedriver:版本需和自己的chrome浏览器对应, 下载地址: https://chromedriver.storage.googleapis.com/index.html。 作用:对chrome浏览器进行驱动。
HTMLTestRunner:HTMLTestRunner是Python标准库的unittest模块的一个扩展。它生成易于使用的HTML测试报告。  下载后放在对应的包中,使用import引入即可使用。

项目结构

项目主要包括一下几个部分
  1. config#存放配置文件
    1. config.ini
    2. globalparameter.py#全局变量的py文件
  2. log  #存放项目日志
    1. mylog.log
  3. data #存放测试数据
    • test.xls #测试数据存放
  4. driver #存放项目使用到的驱动
    • chromedriver.exe #chrome浏览器驱动文件
  5. report #存放生成的测试报告
  6. src #存放源代码
    • common #共同的文件
      • HTMLTestRunner.py #用于生成HTML格式的测试报告
      • browser_engine.py#打开关闭浏览器
      • excle_data#封装xlrd的excel数据的读取
      • base_page.py #对浏览器的操作
      • log.py#日志
      • db_connect#链接数据库
      • send_email#发送邮件 
    • pageobject#存放页面元素,页面操作
    • testcase#存放用例
  7. runtest.py#代码启动入口

代码实现

config包configini

# this is config file, only store browser type and server URL
 
[browserType]
#browserName = Firefox
browserName = Chrome
#browserName = IE
 
[testServer]
URL =

[smtp_sever]#邮箱服务器注意:应填写邮箱@之后的地址,例如qq邮箱就写smtp.qq.com
smtp_sever =smtp.qq.com
[email_name]
email_name = 
[email_password]
email_password = 
[email_to]
email_to = 
[dbServer]
dbServer=
[port]
port=3306
[user]
user=root
[password]
password=
[db]
db=
 
 

config包glbalparameter-全局变量

# coding:utf-8
import time,os
 
'''
配置全局参数
'''
 
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
# project_path1=os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)[0]),'.'))
# print (project_path)
# print (project_path1)
 
#配置文件存放路径
# config_file_path = project_path + '/config/config.ini'
config_file_path = project_path + '\\config\\config.ini'
#浏览器驱动存放路径
chrome_driver_path = project_path + '\\driver\\chromedriver.exe'
ie_driver_path = project_path + '\\driver\\IEDriverServer.exe'
 
#execl测试数据文档存放路径
test_data_path=project_path+"\\data\\testData.xlsx"
 
#日志文件存储路径
log_path=project_path+"\\log\\mylog.log"
print ("日志路径:"+log_path)
# 测试报告存储路径,并以当前时间作为报告名称前缀
report_path = project_path+"\\report\\"
report_name = report_path+time.strftime('%Y%m%d%H%S', time.localtime())
# 异常截图存储路径,并以当前时间作为图片名称前缀
img_path = project_path+"\\error_img\\"+time.strftime('%Y%m%d%H%S', time.localtime())
 
#测试用例代码存放路径(用于构建suite,注意该文件夹下的文件都应该以test开头命名)
test_case_path=project_path+"\\src\\testcase"
 
 
#login_username="-------------"
#login_password="-------------"
 
 
 
 
# if __name__=='__main__':
# test1 = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)[0]), '.'))
 
 
src包(源代码)-common(共同使用的)
其中,src需要进行初始化,否则,在其他模块进行引用时无法找到。在python中定义包的方法是在对应的文件夹下创建一个__init__.py的文件即可。

readexcel.py(网上简单的)

读取excel数据文件
1 import xlrd
2
3
4 # 读取excel文件中对应row和cel数据信息5 def readExcel(path,row,cel):
6 workBook = xlrd.open_workbook(path)
7 sheet = workBook.sheet_by_name("Sheet1")
8 value = sheet.cell(row,cel).value
9 return value
 
 

excel_data.py(我的)

#封装xlrd的excel数据的读取
# coding:utf-8
 
from src.common import log
from config.globalparameter import test_data_path
import xlrd
'''
读取excel文件
'''
 
 
class excel:
def __init__(self):
self.mylog = log.log()
 
def open_excel(self,file):
u'''读取excel文件'''
try:
data = xlrd.open_workbook(file)
return data
except Exception as e:
self.mylog.error(u"打开excel文件失败")
 
def excel_table(self,file, sheetName):
u'''装载list'''
data = self.open_excel(file)
# 通过工作表名称,获取到一个工作表
table = data.sheet_by_name(sheetName)
# 获取行数
Trows = table.nrows
# 获取 第一行数据
Tcolnames = table.row_values(0)
lister = []
for rownumber in range(1,Trows):
row = table.row_values(rownumber)
if row:
app = {}
for i in range(len(Tcolnames)):
app[Tcolnames[i]] = row[i]
lister.append(app)
return lister
 
def get_list(self,sheetname):
try:
data_list = self.excel_table(test_data_path, sheetname)
assert len(data_list)>=0,u'excel标签页:'+sheetname+u'为空'
return data_list
except Exception as e:
self.mylog.error(u'excel标签页:'+sheetname+u'为空')
raise e

base_page.py

#对浏览器的操作
# coding:utf-8
 
import time
from src.common.log import log
from selenium.webdriver.support.select import Select
from config.globalparameter import project_path,img_path
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
from time import sleep
import re
 
class BasePage(object):
""""
定义一个页面基类,让所有页面都继承这个类,封装一些常用的页面操作方法到这个类
"""
def __init__(self,driver):
self.driver=driver
self.mylog=log()
 
# quit browser and end testing
def quit_browse(self):
self.driver.quit()
 
# 浏览器前进操作
def forward(self):
self.driver.forward()
self.mylog.info("Click forward on current page.")
 
# 浏览器后退操作
def back(self):
self.driver.back()
self.mylog.info("Click back on current page.")
 
# 隐式等待
def wait(self, seconds):
self.driver.implicitly_wait(seconds)
self.mylog.info("wait for %d seconds." % seconds)
 
# 点击关闭当前窗口
def close(self):
try:
self.driver.close()
self.mylog.info("Closing and quit the browser.")
except NameError as e:
self.mylog.error("Failed to quit the browser with %s" % e)
 
# 截图,保存图片
def img_screenshot(self, img_name):
try:
self.driver.get_screenshot_as_file(img_path + img_name + '.png')
except:
self.mylog.error(u'截图失败:' + img_name)
 
# 切换到新窗口
def Current_handel(self):
all_handles=self.driver.window_handles
for handle in all_handles:
self.driver.switch_to.window(handle)
 
# 重写find_element方法,增加定位元素的健壮性
def find_element(self, *selector):
try:
# 确保元素是可见的。
# 注意:以下入参为元组的元素,需要加*。Python存在这种特性,就是将入参放在元组里。
WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(selector))
print("self.driver.find_element(*selector)",self.driver.find_element(*selector))
return self.driver.find_element(*selector)
except:
self.mylog.error(u'找不到元素:'+str(selector))
 
#输入,重新send_keys,,先清除后输入
def type(self,selector,text):
try:
# selector = getattr(self, "_%s" % selector)
el=self.find_element(*selector)
el.clear()
el.send_keys(text)
except NameError as e:
self.mylog.error("Failed to type in input box with %s" % e)
 
def click(self,selector):
try:
el=self.find_element(*selector)
el.click()
sleep(3)
except NameError as e:
self.mylog.error("Failed to click the element with %s" % e)
 
 
# 通过value获取下拉菜单元素并点击
def select_element_text(self,selector,text):
try:
el=self.find_element(*selector)
Select(el).select_by_visible_text(text)
except:
self.mylog.error(u'找不到元素:'+str(selector))
 
# 通过index获取下拉菜单元素并点击
def select_element_index(self,selector,index):
try:
el=self.find_element(*selector)
Select(el).select_by_index(index)
except:
self.mylog.error(u'找不到元素:' + str(selector))
 
#获取元素的属性值
def get_Attribute(self,selector,value):
el = self.driver.find_element(selector).get_attribute(value)
return el
 
# 获取元素的文本的值
def get_text(self,selector):
el=self.find_element(*selector).text
return el
 
 
# 校验按钮是否为选中状态
def is_selected(self,selector):
el=self.find_element(*selector)
if el.is_selected():
print(el+"被选中")
else:
print("请重新选中")
 
# 获取网页标题
def get_page_title(self):
# logger.info("Current page title is %s" % self.driver.title)
return self.driver.title
 
 
 
# 获取单个表单元素
def click_table_element(self, selector, text1, index, p_index, a_index):
el = self.find_element(*selector)
table_tr_list = el.find_elements_by_tag_name("tr")
flag = 0
breakflag = False
# 循环table中tr的值
for tr in table_tr_list:
# 切换tr中的text再循环
for i in (tr.text).split(" "):
# 如果i的值=testStop中传入的值
# print("111111")
# print("i的值", i)
if breakflag:
break
if i == text1:
# 去查找当前值的td所在的位置
ul = table_tr_list[flag].find_elements_by_tag_name("td")[index]
# 查找td下p的元素
ee = ul.find_elements_by_tag_name("p")
if len(ee) == 0:
# 邱菊华新增这一行,兼容电桩类型页面的元素
tt = ul.find_elements_by_tag_name("div")
# 循环td下的p元素
n = 0
for j in ee or tt:
if breakflag:
break
# 查看p下面的a元素
n = n + 1
if n == p_index:
tt = j.find_elements_by_tag_name("a")
flagss = 0
# 循环a元素,循环一次后flagss+1,
for o in tt:
flagss = flagss + 1
# 如flagss==2,即第二个a元素
if flagss == a_index:
# 点击o元素,即删除按钮
time.sleep(1)
# o.click()
return o
breakflag = True
break
flag = flag + 1
 
def cc(self, selector, text):
el = self.find_element(selector)
try:
el.send_keys(text)
except NameError as e:
self.mylog.error("Failed to type in input box with %s" % e)
 
# 重写find_elements方法,增加定位元素的健壮性
def find_elements(self, *selector):
try:
# 确保元素是可见的。
# 注意:以下入参为元组的元素,需要加*。Python存在这种特性,就是将入参放在元组里。
WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(selector))
return self.driver.find_elements(*selector)
except:
self.mylog.error(u'找不到元素:' + str(selector))
 
# 批量操作 针对表格 可以传入多个参数(这里采用可变参数来进行判断)
def table_element(self, selector, *text):
# 先获取页面元素,这里是table
print("selector的值",selector)
el = self.find_element(*selector)
# 查看table下的tr
table_tr_list = el.find_elements_by_tag_name("tr")
flag = 0
flags = []
breakflag = False
# 循环table中所有的tr
for tr in table_tr_list:
# 循环一个tr,flag+1
if breakflag:
break
flag = flag + 1
# 循环每一行并切割取值
for i in tr.text.split(" "):
if breakflag:
break
try:
# 如果i 在text里面
if len(text)==0:
print("请输入要删除的值")
elif len(text)==1:
value="".join(tuple(text))
if i==value:
flags.append(flag)
breakflag = True
break
else:
if i in text:
# print(flag)
# 把当前的flag即行数传到flags中
flags.append(flag)
 
except Exception as e:
print("未找到相应的结果")
# 返回flags 供后面的函数调用
return flags
 
 
def ul_li(self,selector):
el=self.find_element(*selector)
return el.find_elements_by_xpath('li')
 
 
def getdigital(self,oristring):
return re.sub("\D", "",oristring)
 
#批量传入元素类型(1:文本框输入 2:下拉框选择 3:时间控件)、元素、值
def add(self,elements):
pass
for i in elements:
if i[0]==1:
self.type(i[1], i[2])
if i[0]==2:
self.select_element_index(i[1],i[2])
 
 
def clear(self,selector):
el = self.find_element(*selector)
el.clear()
 
def get_value(self,selector):
"""获取元素的value属性"""
return self.driver.find_element(*selector).get_attribute("value")

browser_engine.py

打开关闭浏览器方法的封装
# -*- coding:utf-8 -*-
import configparser
import os.path
from selenium import webdriver
from config.globalparameter import img_path,chrome_driver_path,ie_driver_path,project_path,config_file_path
 
 
class BrowserEngine(object):
def __init__(self, driver):
self.driver = driver
 
# read the browser type from config.ini file, return the driver
def open_browser(self, driver):
config = configparser.ConfigParser()
config.read(config_file_path,encoding='UTF-8')
#config.read(config_file_path)#这是原来的
browser = config.get("browserType", "browserName")
# logger.info("You had select %s browser." % browser)
url = config.get("testServer", "URL")
if browser == "Firefox":
driver = webdriver.Firefox()
# logger.info("Starting firefox browser.")
elif browser == "Chrome":
driver = webdriver.Chrome(chrome_driver_path)
# logger.info("Starting Chrome browser.")
elif browser == "IE":
driver = webdriver.Ie(ie_driver_path)
# logger.info("Starting IE browser.")
driver.get(url)
driver.maximize_window()
driver.implicitly_wait(10)
return driver
 
def quit_browser(self):
# logger.info("Now, Close and quit the browser.")
self.driver.quit()
 

db_connect.py数据库链接

# coding:utf-8
 
import psycopg2
import configparser
from config.globalparameter import config_file_path
 
class Db_Connect(object):
def __init__(self):
config=configparser.ConfigParser()
config.read(config_file_path)
self.host=config.get("dbServer","dbServer")
self.user=config.get("user","user")
self.password=config.get("password","password")
self.db=config.get("db","db")
self.port=config.get("port","port")
 
def db_connect(self):
db=psycopg2.connect(host=self.host,user=self.user,password=self.password,db=self.db,port=int(self.port))
return db
 
def db_executesql(self,sql):
db=psycopg2.connect(host=self.host,user=self.user,password=self.password,db=self.db,port=int(self.port))
cur=db.cursor()
try:
cur.execute(sql)
return cur
except Exception as e:
raise e
HTMLTesstRunner.py(测试报告)代码过长可到网上找
 
send_email.py(邮件发送测试报告)
# coding:utf-8
__author__ = 'helen'
import os,smtplib,os.path
from config import globalparameter as gl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from src.common import log
import configparser
 
'''
邮件发送最新的测试报告
'''
 
 
class send_email:
def __init__(self):
self.mylog = log.log()
 
# 定义邮件内容
def email_init(self,report,reportName):
with open(report,'rb')as f:
mail_body = f.read()
config=configparser.ConfigParser()
config.read(gl.config_file_path,encoding='UTF-8')
smtp_sever=config.get("smtp_sever","smtp_sever")
email_password=config.get("email_password","email_password")
email_name=config.get("email_name","email_name")
email_to=config.get("email_to","email_to")
# 创建一个带附件的邮件实例
msg = MIMEMultipart()
# 以测试报告作为邮件正文
msg.attach(MIMEText(mail_body,'html','utf-8'))
report_file = MIMEText(mail_body,'html','utf-8')
# 定义附件名称(附件的名称可以随便定义,你写的是什么邮件里面显示的就是什么)
report_file["Content-Disposition"] = 'attachment;filename='+reportName
msg.attach(report_file) # 添加附件
msg['Subject'] = '自动化测试报告:'+reportName # 邮件标题
msg['From'] = email_name #发件人
msg['To'] = email_to #收件人列表
try:
server = smtplib.SMTP(smtp_sever)
server.login(email_name,email_password)
server.sendmail(msg['From'],msg['To'].split(';'),msg.as_string())
server.quit()
except smtplib.SMTPException:
self.mylog.error(u'邮件发送测试报告失败 at'+__file__)
 
def sendReport(self):
# 找到最新的测试报告
report_list = os.listdir(gl.report_path)
report_list.sort(key=lambda fn: os.path.getmtime(gl.report_path+fn) if not os.path.isdir(gl.report_path+fn) else 0)
new_report = os.path.join(gl.report_path,report_list[-1])
# 发送邮件
self.email_init(new_report,report_list[-1])
log.py(日志)
# coding:utf-8
 
import logging
from config import globalparameter as gl
'''
配置日志文件,输出INFO级别以上的日志
'''
 
 
class log:
def __init__(self):
self.logname = "mylog"
 
def setMSG(self, level, msg):
 
logger = logging.getLogger()
# 定义Handler输出到文件和控制台
fh = logging.FileHandler(gl.log_path)
ch = logging.StreamHandler()
# 定义日志输出格式
formater = logging.Formatter("%(asctime)s %(levelname)s %(message)s' ")
fh.setFormatter(formater)
ch.setFormatter(formater)
# 添加Handler
logger.addHandler(fh)
logger.addHandler(ch)
# 添加日志信息,输出INFO级别的信息
logger.setLevel(logging.INFO)
if level=='debug':
logger.debug(msg)
elif level=='info':
logger.info(msg)
elif level=='warning':
logger.warning(msg)
elif level=='error':
logger.error(msg)
 
# 移除句柄,否则日志会重复输出
logger.removeHandler(fh)
logger.removeHandler(ch)
fh.close()
 
def debug(self, msg):
self.setMSG('debug', msg)
 
def info(self, msg):
self.setMSG('info', msg)
 
def warning(self, msg):
self.setMSG('warning', msg)
 
def error(self, msg):
self.setMSG('error', msg)
 
testcase演示test_search.py
# coding:utf-8
 
import unittest
from src.common.browser_engine import BrowserEngine
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
from src.pageobject.searchpage.searchpage import SearchPage
import time
 
class Search(unittest.TestCase):
@classmethod
def setUpClass(cls):
browser=BrowserEngine(cls)
cls.driver=browser.open_browser(cls)
cls.driver.implicitly_wait(3)
cls.searchpage=SearchPage(cls.driver)
cls.searchpage.goto_searchpage()
 
@classmethod
def tearDownClass(cls):
cls.driver.quit()
 
 
def test01_searchoname(self):
 
#webdriver.ActionChains(self.driver).send_keys(Keys.ENTER).perform()
print('代码执行完成')
time.sleep(3)
测试用例中的相关说明:
  1. setup():每个测试函数运行前运行
  2. teardown():每个测试函数运行完后执行
  3. setUpClass():必须使用@classmethod 装饰器,所有test运行前运行一次
  4. tearDownClass():必须使用@classmethod装饰器,所有test运行完后运行一次

测试用例执行runtest.py

使用HTMLTestRunner执行测试用例,并生成测试报告。
# conding :utf-8
 
import unittest
import time
from config.globalparameter import test_case_path,report_name
from src.common import send_email
from src.common import HTMLTestRunner
"""构建测试套件,并执行测试"""
 
#构建测试集,包含src/testsuite目录下的所有以test开头的.py文件
print (test_case_path)
suite = unittest.defaultTestLoader.discover(start_dir=test_case_path,pattern='test*.py')
 
if __name__=='__main__':
report=report_name+"Report.html"
fb=open(report,'wb')
runner=HTMLTestRunner.HTMLTestRunner(stream=fb,title=u'陕西项目自动化测试报告',description=u'测试Team')
runner.run(suite)
fb.close()
email=send_email.send_email()
email.sendReport()

 

猜你喜欢

转载自www.cnblogs.com/humiao-0626/p/10830377.html