1. 程式人生 > 程式設計 >python+selenium自動化框架搭建的方法步驟

python+selenium自動化框架搭建的方法步驟

環境及使用軟體資訊

  • python 3
  • selenium 3.13.0
  • xlrd 1.1.0
  • chromedriver
  • HTMLTestRunner

說明:

selenium/xlrd只需要再python環境下使用pip install 名稱即可進行對應的安裝。 安裝完成後可使用pip list檢視自己的安裝列表資訊。

chromedriver:版本需和自己的chrome瀏覽器對應,百度下載。 作用:對chrome瀏覽器進行驅動。

HTMLTestRunner:HTMLTestRunner是Python標準庫的unittest模組的一個擴充套件。它生成易於使用的HTML測試報告。 下載後放在對應的包中,使用import引入即可使用。

專案結構

專案主要包括以下幾個部分
config#存放配置檔案

  • config.ini
  • globalparameter.py#全域性變數的py檔案

log #存放專案日誌

  • mylog.log

data #存放測試資料

  • test.xls #測試資料存放

driver #存放專案使用到的驅動

  • chromedriver.exe #chrome瀏覽器驅動檔案

report #存放生成的測試報告

src #存放原始碼

  • common #共同的檔案
    • HTMLTestRunner.py #用於生成HTML格式的測試報告
    • browser_engine.py#開啟關閉瀏覽器
    • excle_data#封裝xlrd的excel資料的讀取
    • base_page.py #對瀏覽器的操作
    • log.py#日誌
    • db_connect#連結資料庫
    • send_email#傳送郵件
    • pageobject#存放頁面元素,頁面操作
  • testcase#存放用例

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資料檔案

import xlrd

# 讀取excel檔案中對應row和cel資料資訊5 def readExcel(path,row,cel):
workBook = xlrd.open_workbook(path)
sheet = workBook.sheet_by_name("Sheet1")
value = sheet.cell(row,cel).value
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,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,index):
try:
el=self.find_element(*selector)
Select(el).select_by_index(index)
except:
self.mylog.error(u'找不到元素:' + str(selector))
 
#獲取元素的屬性值
def get_Attribute(self,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,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,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,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,*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,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,'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)

測試用例中的相關說明:

  • setup():每個測試函式執行前執行
  • teardown():每個測試函式執行完後執行
  • setUpClass():必須使用@classmethod 裝飾器,所有test執行前執行一次
  • 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()

到此這篇關於python+selenium自動化框架搭建的方法步驟的文章就介紹到這了,更多相關python selenium自動化搭建內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!