第一步寿羞,你要建立一個叫做 common.py 的公共的方法類。下面我給出的這段注釋詳細的代碼,就是類似我們使用 Postman 的公共方法的封裝,它可以完成 HTTP 協(xié)議的 GET 請求或 POST 請求的驗證慨灭,并且和你的業(yè)務(wù)無關(guān)。
# 定義一個common的類球及,它的父類是object
class Common(object):
? # common的構(gòu)造函數(shù)
? def __init__(self):
? ? # 被測系統(tǒng)的根路由
? ? self.url_root = 'http://127.0.0.1:12356'
? # 封裝你自己的get請求氧骤,uri是訪問路由,params是get請求的參數(shù)吃引,如果沒有默認為空
? def get(self, uri, params=''):
? ? # 拼湊訪問地址
? ? url = self.url_root + uri + params
? ? # 通過get請求訪問對應(yīng)地址
? ? res = requests.get(url)
? ? # 返回request的Response結(jié)果筹陵,類型為requests的Response類型
? ? return res
? # 封裝你自己的post方法,uri是訪問路由镊尺,params是post請求需要傳遞的參數(shù)朦佩,如果沒有參數(shù)這里為空
? def post(self, uri, params=''):
? ? # 拼湊訪問地址
? ? url = self.url_root + uri
? ? if len(params) > 0:
? ? ? # 如果有參數(shù),那么通過post方式訪問對應(yīng)的url庐氮,并將參數(shù)賦值給requests.post默認參數(shù)data
? ? ? # 返回request的Response結(jié)果语稠,類型為requests的Response類型
? ? ? res = requests.post(url, data=params)
? ? else:
? ? ? # 如果無參數(shù),訪問方式如下
? ? ? # 返回request的Response結(jié)果弄砍,類型為requests的Response類型
? ? ? res = requests.post(url)
? ? return res
接下來仙畦,用你自己的 Common 類,修改第一個接口的單接口測試腳本音婶,就可以得到下面的代碼了慨畸。
# Python代碼中引入requests庫,引入后才可以在你的代碼中使用對應(yīng)的類以及成員函數(shù)
from common import Common
# 首頁的路由
uri = '/'
# 實例化自己的Common
comm = Common()
#調(diào)用你自己在Common封裝的get方法 桃熄,返回結(jié)果存到了response_index中
response_index = comm.get(uri)
# 存儲返回的response_index對象的text屬性存儲了訪問主頁的response信息先口,通過下面打印出來
print('Response內(nèi)容:' + response_index.text)
從這段代碼中你可以看到,與前面對應(yīng)的單接口測試腳本相比瞳收,代碼的行數(shù)有明顯的減少碉京,這也能減少你很多的工作量,與此同時螟深,如果你有任何關(guān)于 HTTP 協(xié)議的操作谐宙,都可以在 Common 類中進行修改和完善。
如果使用你自己剛剛建立的公共類(在我們內(nèi)部有時候喜歡把它叫做輪子界弧,這是源于一句俚語“不用重復(fù)造輪子”凡蜻,因為 Common 類就是重復(fù)被各個檢測代碼使用的“輪子”)修改一下第二個接口的單接口測試腳本,代碼就會變成下面這個樣子:
#登錄頁路由
uri = '/login'
# username變量存儲用戶名參數(shù)
username = 'criss'
# password變量存儲密碼參數(shù)
password = 'criss'
# 拼湊body的參數(shù)
payload = 'username=' + username + '&password=' + password
comm = Common()
response_login = comm.post(uri,params=payload)
print('Response內(nèi)容:' + response_login.text)
當你有一些更加復(fù)雜的腳本時垢箕,你會發(fā)現(xiàn)兩次代碼的變化會變得更明顯划栓,也更易讀。
那么条获。使用我們一起封裝的框架來完成上面的多接口測試后忠荞,就會得到下面的代碼:
# Python代碼中引入requests庫,引入后才可以在你的代碼中使用對應(yīng)的類以及成員函數(shù)
from common import Common
# 建立uri_index的變量,存儲戰(zhàn)場的首頁路由
uri_index = '/'
# 實例化自己的Common
comm = Common()
#調(diào)用你自己在Common封裝的get方法 委煤,返回結(jié)果存到了response_index中
response_index = comm.get(uri_index)
# 存儲返回的response_index對象的text屬性存儲了訪問主頁的response信息堂油,通過下面打印出來
print('Response內(nèi)容:' + response_index.text)
# uri_login存儲戰(zhàn)場的登錄
uri_login = '/login'
# username變量存儲用戶名參數(shù)
username = 'criss'
# password變量存儲密碼參數(shù)
password = 'criss'
# 拼湊body的參數(shù)
payload = 'username=' + username + '&password=' + password
comm = Common()
response_login = comm.post(uri_login,params=payload)
print('Response內(nèi)容:' + response_login.text)
# uri_selectEq存儲戰(zhàn)場的選擇武器
uri_selectEq = '/selectEq'
# 武器編號變量存儲用戶名參數(shù)
equipmentid = '10003'
# 拼湊body的參數(shù)
payload = 'equipmentid=' + equipmentid
comm = Common()
response_selectEq = comm.post(uri_selectEq,params=payload)
print('Response內(nèi)容:' + response_selectEq.text)
# uri_kill存儲戰(zhàn)場的選擇武器
uri_kill = '/kill'
# 武器編號變量存儲用戶名參數(shù)
enemyid = '20001'
# 拼湊body的參數(shù)
payload = 'enemyid=' + enemyid+"&equipmentid="+equipmentid
comm = Common()
response_kill = comm.post(uri_kill,params=payload)
print('Response內(nèi)容:' + response_kill.text)
你可以看到,上面的代碼大量重復(fù)了你自己寫的通用類的調(diào)用碧绞,這個其實是可以合成一個的府框;同時,你再觀察一下我們一起寫的 Common 類讥邻,你會發(fā)現(xiàn)有一個 self.url_root = ‘http://127.0.0.1:12356’迫靖,如果這里這樣寫,你的 Common 就只能用來測試我們這個小系統(tǒng)了计维,除非你每次都去修改框架袜香。
但是,任何一個框架的維護者鲫惶,都不希望框架和具體邏輯強相關(guān)蜈首,因此這也是一個優(yōu)化點,那么將上面的內(nèi)容都修改后欠母,代碼就會變成下面這個樣子:
# Python代碼中引入requests庫欢策,引入后才可以在你的代碼中使用對應(yīng)的類以及成員函數(shù)
from common import Common
# 建立uri_index的變量,存儲戰(zhàn)場的首頁路由
uri_index = '/'
# 實例化自己的Common
comm = Common('http://127.0.0.1:12356')
#調(diào)用你自己在Common封裝的get方法 赏淌,返回結(jié)果存到了response_index中
response_index = comm.get(uri_index)
# 存儲返回的response_index對象的text屬性存儲了訪問主頁的response信息踩寇,通過下面打印出來
print('Response內(nèi)容:' + response_index.text)
# uri_login存儲戰(zhàn)場的登錄
uri_login = '/login'
# username變量存儲用戶名參數(shù)
username = 'criss'
# password變量存儲密碼參數(shù)
password = 'criss'
# 拼湊body的參數(shù)
payload = 'username=' + username + '&password=' + password
response_login = comm.post(uri_login,params=payload)
print('Response內(nèi)容:' + response_login.text)
# uri_selectEq存儲戰(zhàn)場的選擇武器
uri_selectEq = '/selectEq'
# 武器編號變量存儲用戶名參數(shù)
equipmentid = '10003'
# 拼湊body的參數(shù)
payload = 'equipmentid=' + equipmentid
response_selectEq = comm.post(uri_selectEq,params=payload)
print('Response內(nèi)容:' + response_selectEq.text)
# uri_kill存儲戰(zhàn)場的選擇武器
uri_kill = '/kill'
# 武器編號變量存儲用戶名參數(shù)
enemyid = '20001'
# 拼湊body的參數(shù)
payload = 'enemyid=' + enemyid+"&equipmentid="+equipmentid
response_kill = comm.post(uri_kill,params=payload)
print('Response內(nèi)容:' + response_kill.text)
是不是比上一個節(jié)省了很多代碼,同時也看的更加的易讀了六水,那么我們封住好的Common就變成了如下的樣子:
# 定義一個common的類俺孙,它的父類是object
class Common(object):
# common的構(gòu)造函數(shù)
def __init__(self,url_root):
# 被測系統(tǒng)的跟路由
self.url_root = url_root
# 封裝你自己的get請求,uri是訪問路由掷贾,params是get請求的參數(shù)睛榄,如果沒有默認為空
def get(self, uri, params=''):
# 拼湊訪問地址
url = self.url_root + uri + params
# 通過get請求訪問對應(yīng)地址
res = requests.get(url)
# 返回request的Response結(jié)果,類型為requests的Response類型
return res
# 封裝你自己的post方法想帅,uri是訪問路由场靴,params是post請求需要傳遞的參數(shù),如果沒有參數(shù)這里為空
def post(self, uri, params=''):
# 拼湊訪問地址
url = self.url_root + uri
if len(params) > 0:
# 如果有參數(shù)港准,那么通過post方式訪問對應(yīng)的url旨剥,并將參數(shù)賦值給requests.post默認參數(shù)data
# 返回request的Response結(jié)果,類型為requests的Response類型
res = requests.post(url, data=params)
else:
# 如果無參數(shù)浅缸,訪問方式如下
# 返回request的Response結(jié)果轨帜,類型為requests的Response類型
res = req
你可以看到,在上面這段代碼中衩椒,我主要是讓我們 Common 類的構(gòu)造函數(shù)接受了一個變量阵谚,這個變量就是被測系統(tǒng)的根路由蚕礼。這樣是不是就比上一個代碼段節(jié)省了很多代碼烟具,同時也更加易讀了梢什?那么我們封裝好的 Common 就變成了下面這個樣子:
# 定義一個common的類,它的父類是object
class Common(object):
? # common的構(gòu)造函數(shù)
? def __init__(self,url_root):
? ? # 被測系統(tǒng)的跟路由
? ? self.url_root = url_root
? # 封裝你自己的get請求朝聋,uri是訪問路由嗡午,params是get請求的參數(shù),如果沒有默認為空
? def get(self, uri, params=''):
? ? # 拼湊訪問地址
? ? url = self.url_root + uri + params
? ? # 通過get請求訪問對應(yīng)地址
? ? res = requests.get(url)
? ? # 返回request的Response結(jié)果冀痕,類型為requests的Response類型
? ? return res
? # 封裝你自己的post方法斋配,uri是訪問路由刹碾,params是post請求需要傳遞的參數(shù),如果沒有參數(shù)這里為空
? def post(self, uri, params=''):?
? ? # 拼湊訪問地址?
? ? url = self.url_root + uri?
? ? if len(params) > 0:?
? ? ? # 如果有參數(shù),那么通過post方式訪問對應(yīng)的url该园,并將參數(shù)賦值給requests.post默認參數(shù)data
? ? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? ? res = requests.post(url, data=params)?
? ? else:
? ? ? # 如果無參數(shù)鹿蜀,訪問方式如下
? ? ? # 返回request的Response結(jié)果庵佣,類型為requests的Response類型
? ? ? res = requests.post(url)
? ? ? return res
通過改造 Common 類的構(gòu)造函數(shù),這個類已經(jīng)變成一個通用類了婿斥,無論是哪一個項目的接口測試劝篷,都可以使用它來完成 HTTP 協(xié)議的接口驗證了。其實到這里民宿,我們上面說的只能算是一個調(diào)試代碼娇妓,還不能算是一個測試框架。上面這些代碼所有的返回值都打印到控制臺后活鹰,為了完成接口測試哈恰,你需要時時刻刻看著控制臺,這還不能算是自動化志群,只能說是一個輔助小工具着绷。
在這里,你應(yīng)該讓全部測試結(jié)果都存儲到測試報告里面赖舟,同時通過一個測試驅(qū)動框架來完成各個模塊的驅(qū)動蓬戚,比如Python 的 Unittest 。因此宾抓,上面的 Common 類還需要和 Python 的 unittest 一起使用子漩,才算是一個完美的測試框架。
讓你的框架支持RESTful風格的接口
RESTful 接口的測試和原始的 HTTP 協(xié)議接口的測試石洗,又有什么區(qū)別呢幢泼?這里面有兩部分需要你特別關(guān)注:數(shù)據(jù)交換的承載方式和操作方式。
我先說說數(shù)據(jù)交換的承載方式讲衫,RESTful 風格的接口主要是以 JSON 格式來進行數(shù)據(jù)交換缕棵。
另外一個部分是操作方式孵班,上面用了HTTP 協(xié)議的 Get 和 Post,其實 HTTP 協(xié)議有很多方法招驴,但是我們僅僅用了這兩種篙程,而 RESTful 的規(guī)定,使 HTTP 的很多方法都被利用到了别厘,比如說虱饿,Get 方法用來獲取資源,Post 方法用來新建資源(或者更新資源)触趴;再比如說氮发,Put 方法用來更新資源、Delete 方法用來刪除資源等等冗懦。
現(xiàn)在爽冕,我們已經(jīng)可以借助開源庫,解決數(shù)據(jù)交換的事情了披蕉,但是颈畸,RESTful 風格接口和普通 HTTP 接口相比,還有一個明顯的區(qū)別嚣艇,那就是 RESTful 規(guī)定了 HTTP 的每一個方法都做固定的事情承冰,可我們原來框架中的 Common 類卻只支持 Get 和 Post 方法,因此食零,你需要在 Common 類中加入 Delete 和 Put 方法的支持困乒。具體的操作你可以依據(jù)下面這個代碼段來完成:
? def put(self,uri,params=None):
? ? '''
? ? 封裝你自己的put方法,uri是訪問路由贰谣,params是put請求需要傳遞的參數(shù)娜搂,如果沒有參數(shù)這里為空
? ? :param uri: 訪問路由?
? ? :param params: 傳遞參數(shù),string類型吱抚,默認為None?
? ? :return: 此次訪問的response
? ? '''?
? ? url = self.url_root+uri
? ? if params is not None:
? ? ? # 如果有參數(shù)百宇,那么通過put方式訪問對應(yīng)的url,并將參數(shù)賦值給requests.put默認參數(shù)data
? ? ? # 返回request的Response結(jié)果秘豹,類型為requests的Response類型
? ? ? res = requests.put(url, data=params)
? else:
? ? ? # 如果無參數(shù)携御,訪問方式如下
? ? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? ? res = requests.put(url)
? return res
def delete(self,uri,params=None):
? '''
? 封裝你自己的delete方法既绕,uri是訪問路由啄刹,params是delete請求需要傳遞的參數(shù),如果沒有參數(shù)這里為空
? :param uri: 訪問路由
? :param params: 傳遞參數(shù)凄贩,string類型誓军,默認為None
? :return: 此次訪問的response
? '''
? url = self.url_root + uri
? if params is not None:
? ? # 如果有參數(shù),那么通過delete方式訪問對應(yīng)的url疲扎,并將參數(shù)賦值給requests.delete默認參數(shù)data
? ? # 返回request的Response結(jié)果昵时,類型為requests的Response類型
? ? res = requests.delete(url, data=params)
? else:
? ? # 如果無參數(shù)捷雕,訪問方式如下
? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? res = requests.delete(url)
? return res
在上面的代碼中壹甥,你可以看到救巷,我們?yōu)榱藢崿F(xiàn) HTTP 協(xié)議的 Put 和 Delete 方法,自己封裝了 put() 函數(shù)和 delete() 函數(shù)盹廷。其實征绸,要實現(xiàn) RESTful 風格的接口測試,你只要封裝 HTTP 協(xié)議對應(yīng)的 Method 方法就可以了俄占,這樣,你的框架就能完美的支持 RESTful 風格的接口了淆衷。完成了這個操作后缸榄,我們的 Common 類就既可以完成 HTTP 協(xié)議接口的測試,也可以完成 RESTful 接口的測試了祝拯。
將 WebSocket 接口封裝進你的框架
庫甚带,因此我只要用它完成客戶端的撰寫,就可以進行接口測試了佳头。這里鹰贵,我寫下了第一個 WebSocket 的調(diào)用代碼(這里我們以 http://www.websocket.org/demos/echo/ 為例),如下面圖中所示康嘉,我在代碼里面寫了詳細的注釋碉输,你肯定能看懂每一句話的意思。
#引入websocket的create_connection類
from websocket import create_connection
# 建立和WebSocket接口的鏈接
ws = create_connection("ws://echo.websocket.org")
# 打印日子
print("發(fā)送 'Hello, World'...")
# 發(fā)送Hello亭珍,World
ws.send("Hello, World")
# 將WebSocket的返回值存儲result變量
result = ws.recv()
# 打印返回的result
print("返回"+result)
# 關(guān)閉WebSocket鏈接
ws.close()
不知道你發(fā)現(xiàn)沒有敷钾,上面的代碼和 HTTP 協(xié)議的接口類似,都是先和一個請求建立連接肄梨,然后發(fā)送信息阻荒。它們的區(qū)別是,WebSocket 是一個長連接众羡,因此需要人為的建立連接侨赡,然后再關(guān)閉鏈接,而 HTTP 卻并不需要進行這一操作粱侣。
我們上面封裝了 Common 類羊壹,你可以在它的構(gòu)造函數(shù)中,添加一個 API 類型的參數(shù)甜害,以便于知道自己要做的是什么協(xié)議的接口舶掖,其中 http 代表 HTTP 協(xié)議接口,ws 代表 WebSocket 協(xié)議接口尔店。由于 WebSocket 是一個長連接眨攘,我們在 Common 類析構(gòu)函數(shù)中添加了關(guān)閉 ws 鏈接的代碼主慰,以釋放 WebSocket 長連接。依據(jù)前面的交互流程鲫售,實現(xiàn)代碼如下所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# python代碼中引入requests庫共螺,引入后才可以在你的代碼中使用對應(yīng)的類以及成員函數(shù)
import requests
from websocket import create_connection
# 定義一個common的類,它的父類是object
class Common(object):
? # common的構(gòu)造函數(shù)
? def __init__(self,url_root,api_type):
? ? '''
? ? :param api_type:接口類似當前支持http情竹、ws藐不,http就是HTTP協(xié)議,ws是WebSocket協(xié)議
? ? :param url_root: 被測系統(tǒng)的根路由
? ? '''?
? ? if api_type=='ws':
? ? ? self.ws = create_connection(url_root)
? ? elif api_type=='http':
? ? ? self.ws='null'
? ? ? self.url_root = url_root
? # ws協(xié)議的消息發(fā)送
? def send(self,params):
? ? '''
? ? :param params: websocket接口的參數(shù)
? ? :return: 訪問接口的返回值
? ? '''
? ? self.ws.send(params)
? ? res = self.ws.recv()
? ? return res
? # common類的析構(gòu)函數(shù)秦效,清理沒有用的資源
? def __del__(self):
? ? '''
? ? :return:
? ? '''
? ? if self.ws!='null":
? ? ? self.ws.close()
? def get(self, uri, params=None):
? ? '''
? ? 封裝你自己的get請求雏蛮,uri是訪問路由,params是get請求的參數(shù)阱州,如果沒有默認為空
? ? :param uri: 訪問路由
? ? :param params: 傳遞參數(shù)挑秉,string類型,默認為None
? ? :return: 此次訪問的response
? ? '''
? ? # 拼湊訪問地址
? ? if params is not None:
? ? ? url = self.url_root + uri + params
? ? else:?
? ? ? url = self.url_root + uri
? ? # 通過get請求訪問對應(yīng)地址
? ? res = requests.get(url)
? ? # 返回request的Response結(jié)果苔货,類型為requests的Response類型
? ? return res
? def post(self, uri, params=None):
? ? '''
? ? 封裝你自己的post方法犀概,uri是訪問路由,params是post請求需要傳遞的參數(shù)夜惭,如果沒有參數(shù)這里為空
? ? :param uri: 訪問路由
? ? :param params: 傳遞參數(shù)姻灶,string類型,默認為None
? ? :return: 此次訪問的response
? ? '''
? ? # 拼湊訪問地址
? ? url = self.url_root + uri
? ? if params is not None:
? ? ? # 如果有參數(shù)诈茧,那么通過post方式訪問對應(yīng)的url产喉,并將參數(shù)賦值給requests.post默認參數(shù)data
? ? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? ? res = requests.post(url, data=params)
? ? else:
? ? ? # 如果無參數(shù)若皱,訪問方式如下
? ? ? # 返回request的Response結(jié)果镊叁,類型為requests的Response類型
? ? ? res = requests.post(url)?
? ? return res
? def put(self,uri,params=None):
? ? '''
? ? 封裝你自己的put方法,uri是訪問路由走触,params是put請求需要傳遞的參數(shù)晦譬,如果沒有參數(shù)這里為空
? ? :param uri: 訪問路由
? ? :param params: 傳遞參數(shù),string類型互广,默認為None
? ? :return: 此次訪問的response
? ? '''
? ? url = self.url_root+uri
? ? if params is not None:
? ? ? # 如果有參數(shù)敛腌,那么通過put方式訪問對應(yīng)的url,并將參數(shù)賦值給requests.put默認參數(shù)data
? ? ? # 返回request的Response結(jié)果惫皱,類型為requests的Response類型
? ? ? res = requests.put(url, data=params)
? ? else:
? ? ? # 如果無參數(shù)像樊,訪問方式如下
? ? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? ? res = requests.put(url)
? ? return res
? def delete(self,uri,params=None):
? ? '''
? ? 封裝你自己的delete方法旅敷,uri是訪問路由生棍,params是delete請求需要傳遞的參數(shù),如果沒有參數(shù)這里為空
? ? :param uri: 訪問路由
? ? :param params: 傳遞參數(shù)媳谁,string類型涂滴,默認為None
? ? :return: 此次訪問的response
? ? '''
? ? url = self.url_root + uri
? ? if params is not None:
? ? ? # 如果有參數(shù)友酱,那么通過put方式訪問對應(yīng)的url,并將參數(shù)賦值給requests.put默認參數(shù)data
? ? ? # 返回request的Response結(jié)果柔纵,類型為requests的Response類型
? ? ? res = requests.delete(url, data=params)
? ? else:
? ? ? # 如果無參數(shù)缔杉,訪問方式如下
? ? ? # 返回request的Response結(jié)果,類型為requests的Response類型
? ? ? res = requests.put(url)
? ? return res
那么搁料,使用上述的 Common 類將上面那個流水賬一樣的腳本進行改造后或详,就得出了下面這段代碼:
from common import Common
# 建立和WebSocket接口的鏈接
con = Common('ws://echo.websocket.org','ws')
# 獲取返回結(jié)果
result = con.send('Hello, World...')
#打印日志
print(result)
#釋放WebSocket的長連接
del con
現(xiàn)在,從改造后的代碼中郭计,你是不是更能體會到框架的魅力了霸琴?它能讓代碼變得更加簡潔和易讀,將 WebSocket 的協(xié)議封裝到你的框架后拣宏,你就擁有了一個既包含 HTTP 協(xié)議又包含 WebSocket 協(xié)議的接口測試框架了沈贝,隨著你不斷地積累新協(xié)議,你的框架會越來越強大勋乾,你自己的秘密武器庫也會不斷擴充,隨著你對它的不斷完善嗡善,它會讓你的接口測試工作越來越簡單辑莫,越來越快速。
最后罩引,我們需要將數(shù)據(jù)封裝各吨,例如把測試數(shù)據(jù)放在excel里,Excel 是在設(shè)計測試用例方面使用最多的一個工具袁铐,那么我們也就可以用 Excel 作為自己的參數(shù)存儲文件揭蜒。那么如何選取和調(diào)用參數(shù)呢?你可以看看我設(shè)計的參數(shù)類:
import json
import xlrd
class Param(object):
? def __init__(self,paramConf='{}'):
? ? self.paramConf = json.loads(paramConf)
? def paramRowsCount(self):
? ? pass
? def paramColsCount(self):
? ? pass
? def paramHeader(self):
? ? pass
? def paramAllline(self):
? ? pass
? def paramAlllineDict(self):
? ? pass
class XLS(Param):
? '''
? xls基本格式(如果要把xls中存儲的數(shù)字按照文本讀出來的話,純數(shù)字前要加上英文單引號:
? 第一行是參數(shù)的注釋,就是每一行參數(shù)是什么
? 第二行是參數(shù)名,參數(shù)名和對應(yīng)模塊的po頁面的變量名一致
? 第3~N行是參數(shù)
? 最后一列是預(yù)期默認頭Exp
? '''
? def __init__(self, paramConf):
? ? '''
? ? :param paramConf: xls 文件位置(絕對路徑)
? ? '''
? ? self.paramConf = paramConf
? ? self.paramfile = self.paramConf['file']
? ? self.data = xlrd.open_workbook(self.paramfile)
? ? self.getParamSheet(self.paramConf['sheet'])
? def getParamSheet(self,nsheets):
? ? '''
? ? 設(shè)定參數(shù)所處的sheet
? ? :param nsheets: 參數(shù)在第幾個sheet中
? ? :return:
? ? '''
? ? self.paramsheet = self.data.sheets()[nsheets]
? def getOneline(self,nRow):
? ? '''
? ? 返回一行數(shù)據(jù)
? ? :param nRow: 行數(shù)
? ? :return: 一行數(shù)據(jù) []
? ? '''
? ? return self.paramsheet.row_values(nRow)
? def getOneCol(self,nCol):
? ? '''
? ? 返回一列
? ? :param nCol: 列數(shù)
? ? :return: 一列數(shù)據(jù) []
? ? '''
? ? return self.paramsheet.col_values(nCol)
? def paramRowsCount(self):
? ? '''
? ? 獲取參數(shù)文件行數(shù)
? ? :return: 參數(shù)行數(shù) int
? ? '''
? ? return self.paramsheet.nrows
? def paramColsCount(self):
? ? '''
? ? 獲取參數(shù)文件列數(shù)(參數(shù)個數(shù))
? ? :return: 參數(shù)文件列數(shù)(參數(shù)個數(shù)) int
? ? '''
? ? return self.paramsheet.ncols
? def paramHeader(self):
? ? '''
? ? 獲取參數(shù)名稱
? ? :return: 參數(shù)名稱[]
? ? '''
? ? return self.getOneline(1)
? def paramAlllineDict(self):
? ? '''
? ? 獲取全部參數(shù)
? ? :return: {{}},其中dict的key值是header的值
? ? '''
? ? nCountRows = self.paramRowsCount()
? ? nCountCols = self.paramColsCount()
? ? ParamAllListDict = {}
? ? iRowStep = 2
? ? iColStep = 0
? ? ParamHeader= self.paramHeader()
? ? while iRowStep < nCountRows:
? ? ParamOneLinelist=self.getOneline(iRowStep)
? ? ParamOnelineDict = {}
? ? while iColStep<nCountCols:
? ? ParamOnelineDict[ParamHeader[iColStep]]=ParamOneLinelist[iColStep]
? ? iColStep=iColStep+1
? ? iColStep=0
? ? ParamAllListDict[iRowStep-2]=ParamOnelineDict
? ? iRowStep=iRowStep+1
? ? return ParamAllListDict
? def paramAllline(self):
? ? '''? ? 獲取全部參數(shù)
? ? :return: 全部參數(shù)[[]]? '''
? ? nCountRows= self.paramRowsCount()
? ? paramall = []
? ? iRowStep =2
? ? while iRowStep<nCountRows:
? ? paramall.append(self.getOneline(iRowStep))
? ? iRowStep=iRowStep+1
? ? return paramall
? def __getParamCell(self,numberRow,numberCol):
? ? return self.paramsheet.cell_value(numberRow,numberCol)
class ParamFactory(object):
? def chooseParam(self,type,paramConf):
? ? map_ = {
? ? 'xls': XLS(paramConf)
? ? }
? ? return map_[type
上面這個代碼看著很多剔桨,但你不需要完全看得懂屉更,你只需要知道它解決問題的思路和方法就可以了,思路就是通過統(tǒng)一抽象洒缀,建立一個公共處理數(shù)據(jù)的方式瑰谜。你可以設(shè)計和使用簡單工廠類的設(shè)計模式,這樣如果多一種參數(shù)存儲類型树绩,再添加一個對應(yīng)的處理類就可以了萨脑,這很便于你做快速擴展,也可以一勞永逸地提供統(tǒng)一數(shù)據(jù)的處理模式饺饭。
接下來渤早,你就可以把這次測試的全部參數(shù)都存到 Excel 里面了,具體內(nèi)容如下圖所示:
通過上面的參數(shù)類你可以看出瘫俊,在這個 Excel 文件中鹊杖,第一行是給人讀取的每一列參數(shù)的注釋悴灵,而所有的 Excel 都是從第二行開始讀取的,第二行是參數(shù)名和固定的表示預(yù)期結(jié)果的 exp〗鍪纾現(xiàn)在称勋,我們使用 ParamFactory 類,再配合上面的這個 Excel涯竟,就可以完成”戰(zhàn)場“系統(tǒng)“選擇武器”接口的改造了赡鲜,如下面這段代碼所示:
#引入Common、ParamFactory類
from common import Common
from param import ParamFactory
import os
# uri_login存儲戰(zhàn)場的選擇武器
uri_selectEq = '/selectEq'
comm = Common('http://127.0.0.1:12356',api_type='http')
# 武器編號變量存儲武器編號庐船,并且驗證返回時是否有參數(shù)設(shè)計預(yù)期結(jié)果
# 獲取當前路徑絕對值
curPath = os.path.abspath('.')
# 定義存儲參數(shù)的excel文件路徑
searchparamfile = curPath+'/equipmentid_param.xls'
# 調(diào)用參數(shù)類完成參數(shù)讀取银酬,返回是一個字典,包含全部的excel數(shù)據(jù)除去excel的第一行表頭說明
searchparam_dict = ParamFactory().chooseParam('xls',{'file':searchparamfile,'sheet':0}).paramAlllineDict()
i=0
while i<len(searchparam_dict):
? # 讀取通過參數(shù)類獲取的第i行的參數(shù)
? payload = 'equipmentid=' + searchparam_dict[i]['equipmentid']
? # 讀取通過參數(shù)類獲取的第i行的預(yù)期
? exp=searchparam_dict[i]['exp']
? # 進行接口測試
? response_selectEq = comm.post(uri_selectEq,params=payload)
? # 打印返回結(jié)果
? print('Response內(nèi)容:' + response_selectEq.text)
? # 讀取下一行excel中的數(shù)據(jù)
? i=i+1
這樣再執(zhí)行你的測試腳本筐钟,你就可以看到數(shù)據(jù)文件中的三條數(shù)據(jù)揩瞪,已經(jīng)都會順序的自動執(zhí)行了。那么后續(xù)如果將它付諸于你自己的技術(shù)棧篓冲,以及自己的測試驅(qū)動框架比如 Python 的unittest李破,你就可以通過斷言完成預(yù)期結(jié)果的自動驗證了。